From 4c6e05b2c1aaaaa812960af692e24d9e38a760ae Mon Sep 17 00:00:00 2001 From: James Woglom Date: Wed, 21 Apr 2021 00:44:50 -0400 Subject: [PATCH] tests: add Nightscout fake, tests for basal processing --- Pipfile | 3 +- tests/api/fake.py | 14 +--- tests/nightscout_fake.py | 31 ++++++++ tests/sync/test_basal.py | 10 ++- tests/test_process.py | 162 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 207 insertions(+), 13 deletions(-) create mode 100644 tests/nightscout_fake.py create mode 100644 tests/test_process.py diff --git a/Pipfile b/Pipfile index 5656904..5724e2b 100644 --- a/Pipfile +++ b/Pipfile @@ -13,4 +13,5 @@ lxml = "*" python-dotenv = "*" [scripts] -tconnectsync = "python3 main.py" \ No newline at end of file +tconnectsync = "python3 main.py" +test = "python3 -m unittest discover" \ No newline at end of file diff --git a/tests/api/fake.py b/tests/api/fake.py index 45c9aea..8505842 100644 --- a/tests/api/fake.py +++ b/tests/api/fake.py @@ -1,11 +1,9 @@ import tconnectsync.api class ControlIQApi(tconnectsync.api.controliq.ControlIQApi): - BASE_URL = 'invalid://' - LOGIN_URL = 'invalid://' - def __init__(self): - pass + self.BASE_URL = 'invalid://' + self.LOGIN_URL = 'invalid://' def login(self, email, password): raise NotImplementedError @@ -14,10 +12,8 @@ class ControlIQApi(tconnectsync.api.controliq.ControlIQApi): raise NotImplementedError class WS2Api(tconnectsync.api.ws2.WS2Api): - BASE_URL = 'invalid://' - def __init__(self): - pass + self.BASE_URL = 'invalid://' def get(self, endpoint, query): raise NotImplementedError @@ -26,10 +22,8 @@ class WS2Api(tconnectsync.api.ws2.WS2Api): raise NotImplementedError class AndroidApi(tconnectsync.api.android.AndroidApi): - BASE_URL = 'invalid://' - def __init__(self): - pass + self.BASE_URL = 'invalid://' def login(self, email, password): raise NotImplementedError diff --git a/tests/nightscout_fake.py b/tests/nightscout_fake.py new file mode 100644 index 0000000..37f92d7 --- /dev/null +++ b/tests/nightscout_fake.py @@ -0,0 +1,31 @@ +import collections + +import tconnectsync.nightscout + +class NightscoutApi(tconnectsync.nightscout.NightscoutApi): + def __init__(self): + self.url = 'invalid://' + self.secret = 'invalid' + + self.uploaded_entries = collections.defaultdict(list) + self.deleted_entries = collections.defaultdict(list) + self.put_entries = collections.defaultdict(list) + + def upload_entry(self, ns_format, entity='treatments'): + self.uploaded_entries[entity].append(ns_format) + + def delete_entry(self, ns_format, entity): + self.deleted_entries[entity].append(ns_format) + + def put_entry(self, ns_format, entity): + self.put_entries[entity].append(ns_format) + + def last_uploaded_entry(self, eventType): + raise NotImplementedError + + def last_uploaded_activity(self, activityType): + raise NotImplementedError + + def api_status(self): + raise NotImplementedError + diff --git a/tests/sync/test_basal.py b/tests/sync/test_basal.py index 850080d..603619a 100644 --- a/tests/sync/test_basal.py +++ b/tests/sync/test_basal.py @@ -18,8 +18,9 @@ class TestBasalSync(unittest.TestCase): "pumpFeatures": [] } - def test_process_ciq_basal_events(self): - data = self.base.copy() + @staticmethod + def get_example_ciq_basal_events(): + data = TestBasalSync.base.copy() data["basal"]["tempDeliveryEvents"] = [ { "y": 0.8, @@ -55,6 +56,11 @@ class TestBasalSync(unittest.TestCase): }, ] + return data + + def test_process_ciq_basal_events(self): + data = TestBasalSync.get_example_ciq_basal_events() + basalEvents = process_ciq_basal_events(data) self.assertEqual(len(basalEvents), 4) diff --git a/tests/test_process.py b/tests/test_process.py new file mode 100644 index 0000000..84075bc --- /dev/null +++ b/tests/test_process.py @@ -0,0 +1,162 @@ +#!/usr/bin/env python3 + +import unittest +import datetime +import pprint + +from tconnectsync.process import process_time_range +from tconnectsync.parser.nightscout import NightscoutEntry + +from .api.fake import TConnectApi +from .nightscout_fake import NightscoutApi +from .sync.test_basal import TestBasalSync + +class TestProcessTimeRange(unittest.TestCase): + maxDiff = None + + def stub_therapy_timeline(self, time_start, time_end): + pass + + def stub_therapy_timeline_csv(self, time_start, time_end): + return { + "readingData": [], + "iobData": [], + "basalData": [], + "bolusData": [] + } + + def stub_last_uploaded_entry(self, event_type): + return None + + def stub_last_uploaded_activity(self, activity_type): + return None + + """No data in Nightscout. Uploads all basal data from tconnect.""" + def test_new_ciq_basal_data(self): + tconnect = TConnectApi() + + start = datetime.datetime(2021, 4, 20, 12, 0) + end = datetime.datetime(2021, 4, 21, 12, 0) + + def fake_therapy_timeline(time_start, time_end): + self.assertEqual(time_start, start) + self.assertEqual(time_end, end) + + return TestBasalSync.get_example_ciq_basal_events() + + tconnect.controliq.therapy_timeline = fake_therapy_timeline + tconnect.ws2.therapy_timeline_csv = self.stub_therapy_timeline_csv + + nightscout = NightscoutApi() + + nightscout.last_uploaded_entry = self.stub_last_uploaded_entry + nightscout.last_uploaded_activity = self.stub_last_uploaded_activity + + process_time_range(tconnect, nightscout, start, end, pretend=False) + + self.assertEqual(len(nightscout.uploaded_entries["treatments"]), 4) + self.assertDictEqual(nightscout.uploaded_entries, { + "treatments": [ + NightscoutEntry.basal(0.8, 20.35, "2021-03-16 00:00:00-04:00", reason="tempDelivery"), + NightscoutEntry.basal(0.799, 5.0, "2021-03-16 00:20:21-04:00", reason="profileDelivery"), + NightscoutEntry.basal(0.797, 5.0, "2021-03-16 00:25:21-04:00", reason="algorithmDelivery"), + NightscoutEntry.basal(0, 2693/60, "2021-03-16 00:30:21-04:00", reason="algorithmDelivery") + ]}) + self.assertDictEqual(nightscout.put_entries, {}) + self.assertDictEqual(nightscout.deleted_entries, {}) + + + """Two basal entries in Nightscout. Two new basal entries in tconnect.""" + def test_partial_ciq_basal_data(self): + tconnect = TConnectApi() + + start = datetime.datetime(2021, 4, 20, 12, 0) + end = datetime.datetime(2021, 4, 21, 12, 0) + + def fake_therapy_timeline(time_start, time_end): + self.assertEqual(time_start, start) + self.assertEqual(time_end, end) + + return TestBasalSync.get_example_ciq_basal_events() + + tconnect.controliq.therapy_timeline = fake_therapy_timeline + tconnect.ws2.therapy_timeline_csv = self.stub_therapy_timeline_csv + + nightscout = NightscoutApi() + + def fake_last_uploaded_entry(event_type): + if event_type == "Temp Basal": + return { + "created_at": "2021-03-16 00:20:21-04:00", + "duration": 5 + } + + nightscout.last_uploaded_entry = fake_last_uploaded_entry + nightscout.last_uploaded_activity = self.stub_last_uploaded_activity + + process_time_range(tconnect, nightscout, start, end, pretend=False) + + self.assertEqual(len(nightscout.uploaded_entries["treatments"]), 2) + self.assertDictEqual(nightscout.uploaded_entries, { + "treatments": [ + NightscoutEntry.basal(0.797, 5.0, "2021-03-16 00:25:21-04:00", reason="algorithmDelivery"), + NightscoutEntry.basal(0, 2693/60, "2021-03-16 00:30:21-04:00", reason="algorithmDelivery") + ]}) + self.assertDictEqual(nightscout.put_entries, {}) + self.assertDictEqual(nightscout.deleted_entries, {}) + + + """ + Two basal entries in Nightscout, the latter which needs to be updated + with a longer duration. Two entirely new entries in tconnect.""" + def test_with_updated_duration_ciq_basal_data(self): + tconnect = TConnectApi() + + start = datetime.datetime(2021, 4, 20, 12, 0) + end = datetime.datetime(2021, 4, 21, 12, 0) + + def fake_therapy_timeline(time_start, time_end): + self.assertEqual(time_start, start) + self.assertEqual(time_end, end) + + return TestBasalSync.get_example_ciq_basal_events() + + tconnect.controliq.therapy_timeline = fake_therapy_timeline + tconnect.ws2.therapy_timeline_csv = self.stub_therapy_timeline_csv + + nightscout = NightscoutApi() + + def fake_last_uploaded_entry(event_type): + if event_type == "Temp Basal": + return { + "created_at": "2021-03-16 00:20:21-04:00", + "duration": 3, + "_id": "nightscout_id" + } + + nightscout.last_uploaded_entry = fake_last_uploaded_entry + nightscout.last_uploaded_activity = self.stub_last_uploaded_activity + + process_time_range(tconnect, nightscout, start, end, pretend=False) + + self.assertEqual(len(nightscout.uploaded_entries["treatments"]), 2) + self.assertDictEqual(nightscout.uploaded_entries, { + "treatments": [ + NightscoutEntry.basal(0.797, 5.0, "2021-03-16 00:25:21-04:00", reason="algorithmDelivery"), + NightscoutEntry.basal(0, 2693/60, "2021-03-16 00:30:21-04:00", reason="algorithmDelivery") + ]}) + self.assertEqual(len(nightscout.put_entries["treatments"]), 1) + self.assertDictEqual(nightscout.put_entries, { + "treatments": [ + { + "_id": "nightscout_id", + **NightscoutEntry.basal(0.799, 5.0, "2021-03-16 00:20:21-04:00", reason="profileDelivery") + } + ] + }) + self.assertDictEqual(nightscout.deleted_entries, {}) + + + +if __name__ == '__main__': + unittest.main() \ No newline at end of file