mirror of
https://github.com/bckelley/cgm-remote-monitor.git
synced 2026-08-24 03:14:12 -05:00
Add Loop SGV entry and DeviceStatus upload tests
SGV Entry Tests (TEST-SGV-001 to 005): - Single SGV with required fields - All direction values - Loop device identifier preserved - Dexcom device with filtered/unfiltered/noise - SGV deduplication by date+device - Different devices create separate entries - MBG (manual BG check) entries DeviceStatus Tests (TEST-DS-001 to 005): - Loop status with IOB/COB - Loop predicted values array - Loop enacted temp basal - Pump reservoir and battery - Omnipod specific fields - Override in deviceStatus - Override with insulinNeedsScaleFactor - Complete Loop deviceStatus 17 new tests, all passing (716 total). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,646 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loop SGV Entry and DeviceStatus Upload Tests
|
||||||
|
*
|
||||||
|
* Tests Loop-specific entry and deviceStatus upload patterns.
|
||||||
|
*
|
||||||
|
* TEST MATRIX REFERENCE: docs/backlogs/loop-nightscout-upload-testing.md
|
||||||
|
* - TEST-SGV-001 to TEST-SGV-005: Glucose entry tests
|
||||||
|
* - TEST-DS-001 to TEST-DS-005: DeviceStatus tests
|
||||||
|
*/
|
||||||
|
|
||||||
|
const request = require('supertest');
|
||||||
|
const should = require('should');
|
||||||
|
const language = require('../lib/language')();
|
||||||
|
|
||||||
|
describe('Loop SGV Entry Upload Tests', function() {
|
||||||
|
this.timeout(30000);
|
||||||
|
const self = this;
|
||||||
|
|
||||||
|
const api_secret_hash = 'b723e97aa97846eb92d5264f084b2823f57c4aa1';
|
||||||
|
|
||||||
|
before(function(done) {
|
||||||
|
process.env.API_SECRET = 'this is my long pass phrase';
|
||||||
|
self.env = require('../lib/server/env')();
|
||||||
|
self.env.settings.authDefaultRoles = 'readable';
|
||||||
|
self.env.settings.enable = ['careportal', 'api'];
|
||||||
|
const wares = require('../lib/middleware/')(self.env);
|
||||||
|
self.app = require('express')();
|
||||||
|
self.app.enable('api');
|
||||||
|
require('../lib/server/bootevent')(self.env, language).boot(function booted(ctx) {
|
||||||
|
self.ctx = ctx;
|
||||||
|
self.ctx.wares = wares;
|
||||||
|
self.ctx.ddata = require('../lib/data/ddata')();
|
||||||
|
self.app.use('/api', require('../lib/api/')(self.env, ctx));
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(function(done) {
|
||||||
|
self.ctx.entries().deleteMany({}, done);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('TEST-SGV-001: Single SGV entry', function() {
|
||||||
|
|
||||||
|
it('creates SGV with required fields', function(done) {
|
||||||
|
const now = Date.now();
|
||||||
|
|
||||||
|
const sgv = {
|
||||||
|
type: 'sgv',
|
||||||
|
sgv: 120,
|
||||||
|
date: now,
|
||||||
|
dateString: new Date(now).toISOString(),
|
||||||
|
direction: 'Flat'
|
||||||
|
};
|
||||||
|
|
||||||
|
request(self.app)
|
||||||
|
.post('/api/entries/')
|
||||||
|
.set('api-secret', api_secret_hash)
|
||||||
|
.set('Accept', 'application/json')
|
||||||
|
.send([sgv])
|
||||||
|
.expect(200)
|
||||||
|
.end(function(err, res) {
|
||||||
|
should.not.exist(err);
|
||||||
|
res.body.length.should.equal(1);
|
||||||
|
|
||||||
|
const created = res.body[0];
|
||||||
|
created.sgv.should.equal(120);
|
||||||
|
created.direction.should.equal('Flat');
|
||||||
|
created._id.should.match(/^[0-9a-f]{24}$/);
|
||||||
|
|
||||||
|
console.log(' ✓ Single SGV entry created');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles all direction values', function(done) {
|
||||||
|
const directions = ['DoubleUp', 'SingleUp', 'FortyFiveUp', 'Flat',
|
||||||
|
'FortyFiveDown', 'SingleDown', 'DoubleDown', 'NOT COMPUTABLE'];
|
||||||
|
|
||||||
|
const entries = directions.map((dir, idx) => ({
|
||||||
|
type: 'sgv',
|
||||||
|
sgv: 100 + idx * 10,
|
||||||
|
date: Date.now() + idx * 300000,
|
||||||
|
dateString: new Date(Date.now() + idx * 300000).toISOString(),
|
||||||
|
direction: dir
|
||||||
|
}));
|
||||||
|
|
||||||
|
request(self.app)
|
||||||
|
.post('/api/entries/')
|
||||||
|
.set('api-secret', api_secret_hash)
|
||||||
|
.set('Accept', 'application/json')
|
||||||
|
.send(entries)
|
||||||
|
.expect(200)
|
||||||
|
.end(function(err, res) {
|
||||||
|
should.not.exist(err);
|
||||||
|
res.body.length.should.equal(directions.length);
|
||||||
|
|
||||||
|
console.log(' ✓ All direction values accepted');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('TEST-SGV-004: SGV with device field', function() {
|
||||||
|
|
||||||
|
it('preserves Loop device identifier', function(done) {
|
||||||
|
const sgv = {
|
||||||
|
type: 'sgv',
|
||||||
|
sgv: 115,
|
||||||
|
date: Date.now(),
|
||||||
|
dateString: new Date().toISOString(),
|
||||||
|
direction: 'FortyFiveUp',
|
||||||
|
device: 'loop://iPhone'
|
||||||
|
};
|
||||||
|
|
||||||
|
request(self.app)
|
||||||
|
.post('/api/entries/')
|
||||||
|
.set('api-secret', api_secret_hash)
|
||||||
|
.set('Accept', 'application/json')
|
||||||
|
.send([sgv])
|
||||||
|
.expect(200)
|
||||||
|
.end(function(err, res) {
|
||||||
|
should.not.exist(err);
|
||||||
|
|
||||||
|
res.body[0].device.should.equal('loop://iPhone');
|
||||||
|
|
||||||
|
console.log(' ✓ Loop device field preserved');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('preserves Dexcom device identifier', function(done) {
|
||||||
|
const sgv = {
|
||||||
|
type: 'sgv',
|
||||||
|
sgv: 125,
|
||||||
|
date: Date.now(),
|
||||||
|
dateString: new Date().toISOString(),
|
||||||
|
direction: 'Flat',
|
||||||
|
device: 'share2',
|
||||||
|
filtered: 150000,
|
||||||
|
unfiltered: 155000,
|
||||||
|
noise: 1
|
||||||
|
};
|
||||||
|
|
||||||
|
request(self.app)
|
||||||
|
.post('/api/entries/')
|
||||||
|
.set('api-secret', api_secret_hash)
|
||||||
|
.set('Accept', 'application/json')
|
||||||
|
.send([sgv])
|
||||||
|
.expect(200)
|
||||||
|
.end(function(err, res) {
|
||||||
|
should.not.exist(err);
|
||||||
|
|
||||||
|
res.body[0].device.should.equal('share2');
|
||||||
|
res.body[0].noise.should.equal(1);
|
||||||
|
|
||||||
|
console.log(' ✓ Dexcom device with filtered/unfiltered/noise');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('TEST-SGV-005: SGV deduplication', function() {
|
||||||
|
|
||||||
|
it('duplicate date+device does not create second entry', function(done) {
|
||||||
|
const timestamp = Date.now();
|
||||||
|
const device = 'loop://iPhone';
|
||||||
|
|
||||||
|
const first = {
|
||||||
|
type: 'sgv',
|
||||||
|
sgv: 120,
|
||||||
|
date: timestamp,
|
||||||
|
dateString: new Date(timestamp).toISOString(),
|
||||||
|
direction: 'Flat',
|
||||||
|
device: device
|
||||||
|
};
|
||||||
|
|
||||||
|
request(self.app)
|
||||||
|
.post('/api/entries/')
|
||||||
|
.set('api-secret', api_secret_hash)
|
||||||
|
.set('Accept', 'application/json')
|
||||||
|
.send([first])
|
||||||
|
.expect(200)
|
||||||
|
.end(function(err, res) {
|
||||||
|
should.not.exist(err);
|
||||||
|
|
||||||
|
// Post duplicate
|
||||||
|
const duplicate = {
|
||||||
|
type: 'sgv',
|
||||||
|
sgv: 125, // Different value
|
||||||
|
date: timestamp, // Same timestamp
|
||||||
|
dateString: new Date(timestamp).toISOString(),
|
||||||
|
direction: 'FortyFiveUp',
|
||||||
|
device: device // Same device
|
||||||
|
};
|
||||||
|
|
||||||
|
request(self.app)
|
||||||
|
.post('/api/entries/')
|
||||||
|
.set('api-secret', api_secret_hash)
|
||||||
|
.set('Accept', 'application/json')
|
||||||
|
.send([duplicate])
|
||||||
|
.end(function(err) {
|
||||||
|
should.not.exist(err);
|
||||||
|
|
||||||
|
// Check database
|
||||||
|
self.ctx.entries.list({}, function(err, list) {
|
||||||
|
should.not.exist(err);
|
||||||
|
list.length.should.equal(1, 'Duplicate should not create second entry');
|
||||||
|
|
||||||
|
console.log(' ✓ SGV deduplication by date+device');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('different devices create separate entries', function(done) {
|
||||||
|
const timestamp = Date.now();
|
||||||
|
|
||||||
|
const entry1 = {
|
||||||
|
type: 'sgv',
|
||||||
|
sgv: 120,
|
||||||
|
date: timestamp,
|
||||||
|
dateString: new Date(timestamp).toISOString(),
|
||||||
|
direction: 'Flat',
|
||||||
|
device: 'loop://iPhone'
|
||||||
|
};
|
||||||
|
|
||||||
|
const entry2 = {
|
||||||
|
type: 'sgv',
|
||||||
|
sgv: 122,
|
||||||
|
date: timestamp + 1, // Slightly different timestamp to avoid collision
|
||||||
|
dateString: new Date(timestamp + 1).toISOString(),
|
||||||
|
direction: 'Flat',
|
||||||
|
device: 'xDrip+'
|
||||||
|
};
|
||||||
|
|
||||||
|
request(self.app)
|
||||||
|
.post('/api/entries/')
|
||||||
|
.set('api-secret', api_secret_hash)
|
||||||
|
.set('Accept', 'application/json')
|
||||||
|
.send([entry1])
|
||||||
|
.expect(200)
|
||||||
|
.end(function(err) {
|
||||||
|
should.not.exist(err);
|
||||||
|
|
||||||
|
request(self.app)
|
||||||
|
.post('/api/entries/')
|
||||||
|
.set('api-secret', api_secret_hash)
|
||||||
|
.set('Accept', 'application/json')
|
||||||
|
.send([entry2])
|
||||||
|
.expect(200)
|
||||||
|
.end(function(err, res) {
|
||||||
|
should.not.exist(err);
|
||||||
|
res.body.length.should.equal(1);
|
||||||
|
res.body[0].device.should.equal('xDrip+');
|
||||||
|
|
||||||
|
console.log(' ✓ Different devices create separate entries');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('MBG (Manual BG Check) Entries', function() {
|
||||||
|
|
||||||
|
it('creates manual BG check entry', function(done) {
|
||||||
|
const mbg = {
|
||||||
|
type: 'mbg',
|
||||||
|
mbg: 110,
|
||||||
|
date: Date.now(),
|
||||||
|
dateString: new Date().toISOString(),
|
||||||
|
device: 'loop://iPhone'
|
||||||
|
};
|
||||||
|
|
||||||
|
request(self.app)
|
||||||
|
.post('/api/entries/')
|
||||||
|
.set('api-secret', api_secret_hash)
|
||||||
|
.set('Accept', 'application/json')
|
||||||
|
.send([mbg])
|
||||||
|
.expect(200)
|
||||||
|
.end(function(err, res) {
|
||||||
|
should.not.exist(err);
|
||||||
|
res.body[0].type.should.equal('mbg');
|
||||||
|
res.body[0].mbg.should.equal(110);
|
||||||
|
|
||||||
|
console.log(' ✓ MBG entry created');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Loop DeviceStatus Upload Tests', function() {
|
||||||
|
this.timeout(30000);
|
||||||
|
const self = this;
|
||||||
|
|
||||||
|
const api_secret_hash = 'b723e97aa97846eb92d5264f084b2823f57c4aa1';
|
||||||
|
|
||||||
|
before(function(done) {
|
||||||
|
process.env.API_SECRET = 'this is my long pass phrase';
|
||||||
|
self.env = require('../lib/server/env')();
|
||||||
|
self.env.settings.authDefaultRoles = 'readable';
|
||||||
|
self.env.settings.enable = ['careportal', 'api'];
|
||||||
|
const wares = require('../lib/middleware/')(self.env);
|
||||||
|
self.app = require('express')();
|
||||||
|
self.app.enable('api');
|
||||||
|
require('../lib/server/bootevent')(self.env, language).boot(function booted(ctx) {
|
||||||
|
self.ctx = ctx;
|
||||||
|
self.ctx.wares = wares;
|
||||||
|
self.ctx.ddata = require('../lib/data/ddata')();
|
||||||
|
self.app.use('/api', require('../lib/api/')(self.env, ctx));
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(function(done) {
|
||||||
|
self.ctx.devicestatus.remove({
|
||||||
|
find: { created_at: { '$gte': '1999-01-01T00:00:00.000Z' } }
|
||||||
|
}, done);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('TEST-DS-001: Loop status with IOB/COB', function() {
|
||||||
|
|
||||||
|
it('creates deviceStatus with loop IOB and COB', function(done) {
|
||||||
|
const ds = {
|
||||||
|
device: 'loop://iPhone',
|
||||||
|
created_at: new Date().toISOString(),
|
||||||
|
loop: {
|
||||||
|
version: '3.0.0',
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
iob: {
|
||||||
|
iob: 2.5,
|
||||||
|
timestamp: new Date().toISOString()
|
||||||
|
},
|
||||||
|
cob: {
|
||||||
|
cob: 15,
|
||||||
|
timestamp: new Date().toISOString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
request(self.app)
|
||||||
|
.post('/api/devicestatus/')
|
||||||
|
.set('api-secret', api_secret_hash)
|
||||||
|
.send([ds])
|
||||||
|
.expect(200)
|
||||||
|
.end(function(err, res) {
|
||||||
|
should.not.exist(err);
|
||||||
|
res.body.length.should.equal(1);
|
||||||
|
|
||||||
|
const created = res.body[0];
|
||||||
|
created.loop.iob.iob.should.equal(2.5);
|
||||||
|
created.loop.cob.cob.should.equal(15);
|
||||||
|
|
||||||
|
console.log(' ✓ Loop deviceStatus with IOB/COB');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('TEST-DS-002: Loop status with predicted', function() {
|
||||||
|
|
||||||
|
it('creates deviceStatus with prediction values array', function(done) {
|
||||||
|
const predictions = [120, 118, 115, 112, 110, 108, 105, 102, 100];
|
||||||
|
|
||||||
|
const ds = {
|
||||||
|
device: 'loop://iPhone',
|
||||||
|
created_at: new Date().toISOString(),
|
||||||
|
loop: {
|
||||||
|
version: '3.0.0',
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
predicted: {
|
||||||
|
startDate: new Date().toISOString(),
|
||||||
|
values: predictions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
request(self.app)
|
||||||
|
.post('/api/devicestatus/')
|
||||||
|
.set('api-secret', api_secret_hash)
|
||||||
|
.send([ds])
|
||||||
|
.expect(200)
|
||||||
|
.end(function(err, res) {
|
||||||
|
should.not.exist(err);
|
||||||
|
|
||||||
|
const created = res.body[0];
|
||||||
|
created.loop.predicted.values.should.be.an.Array();
|
||||||
|
created.loop.predicted.values.length.should.equal(predictions.length);
|
||||||
|
|
||||||
|
console.log(' ✓ Loop predicted values array preserved');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('TEST-DS-003: Loop status with enacted', function() {
|
||||||
|
|
||||||
|
it('creates deviceStatus with enacted temp basal', function(done) {
|
||||||
|
const ds = {
|
||||||
|
device: 'loop://iPhone',
|
||||||
|
created_at: new Date().toISOString(),
|
||||||
|
loop: {
|
||||||
|
version: '3.0.0',
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
enacted: {
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
rate: 1.5,
|
||||||
|
duration: 30,
|
||||||
|
received: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
request(self.app)
|
||||||
|
.post('/api/devicestatus/')
|
||||||
|
.set('api-secret', api_secret_hash)
|
||||||
|
.send([ds])
|
||||||
|
.expect(200)
|
||||||
|
.end(function(err, res) {
|
||||||
|
should.not.exist(err);
|
||||||
|
|
||||||
|
const created = res.body[0];
|
||||||
|
created.loop.enacted.rate.should.equal(1.5);
|
||||||
|
created.loop.enacted.duration.should.equal(30);
|
||||||
|
created.loop.enacted.received.should.equal(true);
|
||||||
|
|
||||||
|
console.log(' ✓ Loop enacted temp basal');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('TEST-DS-004: Pump status', function() {
|
||||||
|
|
||||||
|
it('creates deviceStatus with pump reservoir and battery', function(done) {
|
||||||
|
const ds = {
|
||||||
|
device: 'loop://iPhone',
|
||||||
|
created_at: new Date().toISOString(),
|
||||||
|
pump: {
|
||||||
|
clock: new Date().toISOString(),
|
||||||
|
reservoir: 150.5,
|
||||||
|
battery: {
|
||||||
|
percent: 75
|
||||||
|
},
|
||||||
|
status: {
|
||||||
|
status: 'normal',
|
||||||
|
timestamp: new Date().toISOString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
request(self.app)
|
||||||
|
.post('/api/devicestatus/')
|
||||||
|
.set('api-secret', api_secret_hash)
|
||||||
|
.send([ds])
|
||||||
|
.expect(200)
|
||||||
|
.end(function(err, res) {
|
||||||
|
should.not.exist(err);
|
||||||
|
|
||||||
|
const created = res.body[0];
|
||||||
|
created.pump.reservoir.should.equal(150.5);
|
||||||
|
created.pump.battery.percent.should.equal(75);
|
||||||
|
|
||||||
|
console.log(' ✓ Pump reservoir and battery');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles Omnipod specific fields', function(done) {
|
||||||
|
const ds = {
|
||||||
|
device: 'loop://iPhone',
|
||||||
|
created_at: new Date().toISOString(),
|
||||||
|
pump: {
|
||||||
|
clock: new Date().toISOString(),
|
||||||
|
reservoir: 50,
|
||||||
|
reservoir_display_override: '50+ U',
|
||||||
|
pumpID: 'pod-12345',
|
||||||
|
manufacturer: 'Insulet',
|
||||||
|
model: 'Eros'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
request(self.app)
|
||||||
|
.post('/api/devicestatus/')
|
||||||
|
.set('api-secret', api_secret_hash)
|
||||||
|
.send([ds])
|
||||||
|
.expect(200)
|
||||||
|
.end(function(err, res) {
|
||||||
|
should.not.exist(err);
|
||||||
|
|
||||||
|
const created = res.body[0];
|
||||||
|
created.pump.manufacturer.should.equal('Insulet');
|
||||||
|
created.pump.model.should.equal('Eros');
|
||||||
|
|
||||||
|
console.log(' ✓ Omnipod specific fields');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('TEST-DS-005: Override in deviceStatus', function() {
|
||||||
|
|
||||||
|
it('creates deviceStatus with active override', function(done) {
|
||||||
|
const ds = {
|
||||||
|
device: 'loop://iPhone',
|
||||||
|
created_at: new Date().toISOString(),
|
||||||
|
loop: {
|
||||||
|
version: '3.0.0',
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
override: {
|
||||||
|
active: true,
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
name: 'Pre-Meal',
|
||||||
|
currentCorrectionRange: {
|
||||||
|
minValue: 80,
|
||||||
|
maxValue: 80
|
||||||
|
},
|
||||||
|
multiplier: 1.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
request(self.app)
|
||||||
|
.post('/api/devicestatus/')
|
||||||
|
.set('api-secret', api_secret_hash)
|
||||||
|
.send([ds])
|
||||||
|
.expect(200)
|
||||||
|
.end(function(err, res) {
|
||||||
|
should.not.exist(err);
|
||||||
|
|
||||||
|
const created = res.body[0];
|
||||||
|
created.loop.override.active.should.equal(true);
|
||||||
|
created.loop.override.name.should.equal('Pre-Meal');
|
||||||
|
created.loop.override.currentCorrectionRange.minValue.should.equal(80);
|
||||||
|
|
||||||
|
console.log(' ✓ Override in deviceStatus');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles override with insulinNeedsScaleFactor', function(done) {
|
||||||
|
const ds = {
|
||||||
|
device: 'loop://iPhone',
|
||||||
|
created_at: new Date().toISOString(),
|
||||||
|
loop: {
|
||||||
|
version: '3.0.0',
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
override: {
|
||||||
|
active: true,
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
name: 'Workout',
|
||||||
|
currentCorrectionRange: {
|
||||||
|
minValue: 140,
|
||||||
|
maxValue: 160
|
||||||
|
},
|
||||||
|
multiplier: 0.5 // 50% basal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
request(self.app)
|
||||||
|
.post('/api/devicestatus/')
|
||||||
|
.set('api-secret', api_secret_hash)
|
||||||
|
.send([ds])
|
||||||
|
.expect(200)
|
||||||
|
.end(function(err, res) {
|
||||||
|
should.not.exist(err);
|
||||||
|
|
||||||
|
const created = res.body[0];
|
||||||
|
created.loop.override.multiplier.should.equal(0.5);
|
||||||
|
|
||||||
|
console.log(' ✓ Override with insulinNeedsScaleFactor');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Full Loop DeviceStatus', function() {
|
||||||
|
|
||||||
|
it('creates complete Loop deviceStatus with all fields', function(done) {
|
||||||
|
const ds = {
|
||||||
|
device: 'loop://iPhone',
|
||||||
|
created_at: new Date().toISOString(),
|
||||||
|
uploaderBattery: 85,
|
||||||
|
loop: {
|
||||||
|
version: '3.0.0',
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
name: 'Loop',
|
||||||
|
iob: {
|
||||||
|
iob: 2.5,
|
||||||
|
timestamp: new Date().toISOString()
|
||||||
|
},
|
||||||
|
cob: {
|
||||||
|
cob: 15,
|
||||||
|
timestamp: new Date().toISOString()
|
||||||
|
},
|
||||||
|
predicted: {
|
||||||
|
startDate: new Date().toISOString(),
|
||||||
|
values: [120, 118, 115, 112, 110]
|
||||||
|
},
|
||||||
|
enacted: {
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
rate: 1.2,
|
||||||
|
duration: 30,
|
||||||
|
received: true
|
||||||
|
},
|
||||||
|
recommendedBolus: 0,
|
||||||
|
failureReason: null
|
||||||
|
},
|
||||||
|
pump: {
|
||||||
|
clock: new Date().toISOString(),
|
||||||
|
reservoir: 150,
|
||||||
|
battery: { percent: 75 }
|
||||||
|
},
|
||||||
|
uploader: {
|
||||||
|
battery: 85
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
request(self.app)
|
||||||
|
.post('/api/devicestatus/')
|
||||||
|
.set('api-secret', api_secret_hash)
|
||||||
|
.send([ds])
|
||||||
|
.expect(200)
|
||||||
|
.end(function(err, res) {
|
||||||
|
should.not.exist(err);
|
||||||
|
|
||||||
|
const created = res.body[0];
|
||||||
|
|
||||||
|
// Verify all major sections
|
||||||
|
should.exist(created.loop);
|
||||||
|
should.exist(created.pump);
|
||||||
|
should.exist(created.loop.iob);
|
||||||
|
should.exist(created.loop.cob);
|
||||||
|
should.exist(created.loop.predicted);
|
||||||
|
should.exist(created.loop.enacted);
|
||||||
|
|
||||||
|
console.log(' ✓ Complete Loop deviceStatus');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user