mirror of
https://github.com/bckelley/cgm-remote-monitor.git
synced 2026-08-24 03:14:12 -05:00
test(api3): characterize V3 profile dedup behavior with AAPS-shaped payloads
V3 POST /api/v3/profile uses uuid.v5("undefined_<doc.date>") as the
identifier (no device, no eventType in profile docs), so:
- Identical resends (retry) dedup in place (200) — request-level dedup works
- AAPS edits with a new LocalProfileLastChange produce a new identifier and
insert a new doc (201) — edit-level dedup does not exist
This characterizes the V3 behavior alongside the V1 websocket fix in the
prior commit. Both V1 (post-fix) and V3 rely on profile.last() with
{startDate: -1, _id: -1} for deterministic ordering.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -590,4 +590,99 @@ describe('API3 AAPS Patterns - Deduplication and Real-world Scenarios', function
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('Profile sync via REST (V3) - AAPS createProfileStore behavior', function() {
|
||||
const url = '/api/v3/profile';
|
||||
|
||||
function profileCollection() {
|
||||
return self.instance.ctx.store.collection(self.env.profile_collection);
|
||||
}
|
||||
|
||||
function aapsV3Profile(dateMs, overrides) {
|
||||
const iso = new Date(dateMs).toISOString();
|
||||
return Object.assign({
|
||||
app: 'AAPS',
|
||||
date: dateMs,
|
||||
created_at: iso,
|
||||
startDate: iso,
|
||||
defaultProfile: 'aaps-v3-test',
|
||||
units: 'mg/dl',
|
||||
store: {
|
||||
'aaps-v3-test': {
|
||||
dia: 5,
|
||||
carbratio: [{ time: '00:00', value: 10 }],
|
||||
sens: [{ time: '00:00', value: 50 }],
|
||||
basal: [{ time: '00:00', value: 0.5 }],
|
||||
target_low: [{ time: '00:00', value: 100 }],
|
||||
target_high: [{ time: '00:00', value: 120 }],
|
||||
timezone: 'UTC'
|
||||
}
|
||||
}
|
||||
}, overrides || {});
|
||||
}
|
||||
|
||||
async function cleanupTestProfiles() {
|
||||
await profileCollection().deleteMany({ defaultProfile: 'aaps-v3-test' });
|
||||
}
|
||||
|
||||
beforeEach(cleanupTestProfiles);
|
||||
after(cleanupTestProfiles);
|
||||
|
||||
it('first AAPS V3 profile POST returns 201 and inserts a new doc', async () => {
|
||||
const profile = aapsV3Profile(Date.now());
|
||||
const res = await self.instance.post(url, self.jwt.create).send(profile);
|
||||
res.status.should.equal(201);
|
||||
|
||||
const docs = await profileCollection().find({ defaultProfile: 'aaps-v3-test' }).toArray();
|
||||
docs.length.should.equal(1);
|
||||
});
|
||||
|
||||
it('resending the SAME profile (same date) returns 200 and dedups in place', async () => {
|
||||
const profile = aapsV3Profile(Date.now());
|
||||
const res1 = await self.instance.post(url, self.jwt.create).send(profile);
|
||||
res1.status.should.equal(201);
|
||||
self.cache.clear();
|
||||
|
||||
// resend identical payload (e.g. retry after network failure)
|
||||
const res2 = await self.instance.post(url, self.jwt.update).send(profile);
|
||||
res2.status.should.equal(200);
|
||||
res2.body.identifier.should.equal(res1.body.identifier);
|
||||
|
||||
const docs = await profileCollection().find({ defaultProfile: 'aaps-v3-test' }).toArray();
|
||||
docs.length.should.equal(1);
|
||||
});
|
||||
|
||||
it('AAPS edit (new date from LocalProfileLastChange) creates a SECOND doc, not an update', async () => {
|
||||
// Simulates: user edits profile in AAPS twice -> two distinct LocalProfileLastChange values
|
||||
const t1 = Date.now() - 60000;
|
||||
const first = aapsV3Profile(t1);
|
||||
first.store['aaps-v3-test'].carbratio[0].value = 8;
|
||||
const second = aapsV3Profile(Date.now());
|
||||
second.store['aaps-v3-test'].carbratio[0].value = 14;
|
||||
|
||||
const res1 = await self.instance.post(url, self.jwt.create).send(first);
|
||||
res1.status.should.equal(201);
|
||||
self.cache.clear();
|
||||
|
||||
const res2 = await self.instance.post(url, self.jwt.create).send(second);
|
||||
// V3 inserts a NEW doc because identifier (uuidv5 of "undefined_<date>") differs
|
||||
res2.status.should.equal(201);
|
||||
res2.body.identifier.should.not.equal(res1.body.identifier);
|
||||
|
||||
const docs = await profileCollection().find({ defaultProfile: 'aaps-v3-test' }).toArray();
|
||||
docs.length.should.equal(2);
|
||||
|
||||
// Verify ctx.profile.last() returns the newer profile (post-fix sort: startDate desc, _id desc)
|
||||
await new Promise((resolve, reject) => {
|
||||
self.instance.ctx.profile.last((err, lastDocs) => {
|
||||
if (err) return reject(err);
|
||||
try {
|
||||
lastDocs.length.should.equal(1);
|
||||
lastDocs[0].store['aaps-v3-test'].carbratio[0].value.should.equal(14);
|
||||
resolve();
|
||||
} catch (e) { reject(e); }
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user