Files
cgm-remote-monitor/tests/issue-6923-legacy-uuid.test.js
T
Ben WestandCopilot 246e46adb3 fix: legacy UUID treatments findable via $or fallback (#6923)
updateIdQuery() and upsertQueryFor() now use
  {$or: [{identifier: UUID}, {_id: UUID}]}
instead of only {identifier: UUID}. This matches both:
- New documents (UUID in identifier field, ObjectId in _id)
- Legacy documents (UUID directly in _id, no identifier field)

Gated behind env.uuidHandling (UUID_HANDLING env var, default true).

All 30 treatment tests pass:
- 3 legacy UUID tests (issue-6923): DELETE, PUT, GET all work
- 12 gap-treat-012 tests: new data paths unaffected
- 15 uuid-handling tests: edge cases, UUID_HANDLING=false still works

Fixes #6923 (unable to edit/save/delete overrides for legacy data)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-25 15:20:51 -07:00

182 lines
6.4 KiB
JavaScript

'use strict';
/**
* Legacy UUID Data Test: Issue #6923 Regression
*
* ISSUE: https://github.com/nightscout/cgm-remote-monitor/issues/6923
*
* This test verifies behavior when a Temporary Override with a UUID _id
* was created BEFORE the normalizeTreatmentId() fix. Such documents have
* the UUID directly in _id with no identifier field.
*
* The test inserts a legacy-shaped document directly into MongoDB
* (bypassing normalizeTreatmentId) and then exercises the API
* DELETE, PUT, and GET paths that a user would trigger from Reports > Treatments.
*
* FIX: updateIdQuery() and upsertQueryFor() now use $or to match both
* {identifier: UUID} (new docs) and {_id: UUID} (legacy docs).
* All 3 tests pass, confirming Loop can manage pre-existing overrides.
*/
const request = require('supertest');
const should = require('should');
const language = require('../lib/language')();
describe('Issue #6923: Legacy UUID override edit/delete', function () {
this.timeout(30000);
const self = this;
const api_secret_hash = 'b723e97aa97846eb92d5264f084b2823f57c4aa1';
const LEGACY_UUID = '69F15FD2-8075-4DEB-AEA3-4352F455840D';
const LEGACY_OVERRIDE = {
_id: LEGACY_UUID,
eventType: 'Temporary Override',
created_at: '2026-02-17T02:00:16.000Z',
timestamp: '2026-02-17T02:00:16Z',
durationType: 'indefinite',
correctionRange: [90, 110],
insulinNeedsScaleFactor: 1.2,
reason: 'Legacy Override',
enteredBy: 'Loop',
utcOffset: 0
};
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();
});
});
function rawCollection () {
return self.ctx.store.collection(self.env.treatments_collection);
}
beforeEach(function (done) {
rawCollection().deleteMany({}, function () {
done();
});
});
/**
* Insert a legacy-shaped document directly into MongoDB,
* bypassing normalizeTreatmentId(). UUID in _id, no identifier field.
*/
function insertLegacyDoc (callback) {
var doc = Object.assign({}, LEGACY_OVERRIDE);
rawCollection().insertOne(doc, function (err) {
should.not.exist(err);
rawCollection().findOne({ _id: LEGACY_UUID }, function (err, stored) {
should.not.exist(err);
should.exist(stored, 'Legacy doc should exist after direct insert');
stored._id.should.equal(LEGACY_UUID);
should.not.exist(stored.identifier, 'Legacy doc must NOT have identifier field');
callback(stored);
});
});
}
describe('DELETE legacy UUID override via API', function () {
it('DELETE /api/v1/treatments/:uuid should actually remove the legacy document', function (done) {
insertLegacyDoc(function () {
request(self.app)
.delete('/api/treatments/' + LEGACY_UUID)
.set('api-secret', api_secret_hash)
.end(function (err, res) {
should.not.exist(err);
should.exist(res.body);
var deletedCount = res.body.deletedCount || 0;
deletedCount.should.be.above(0,
'DELETE responded 200 but deletedCount is 0. '
+ 'The query layer rewrites {_id: UUID} to {identifier: UUID}, '
+ 'but the legacy document has no identifier field, so deleteMany matches nothing. '
+ 'The override silently persists in the database.'
);
done();
});
});
});
});
describe('PUT (edit/save) legacy UUID override via API', function () {
it('PUT /api/v1/treatments/ with UUID _id should update in place, not create a duplicate', function (done) {
insertLegacyDoc(function () {
var updated = Object.assign({}, LEGACY_OVERRIDE, {
reason: 'Edited Override',
insulinNeedsScaleFactor: 1.5
});
// Fire PUT request — the HTTP response may hang due to data-received
// event processing, but the DB write completes immediately.
request(self.app)
.put('/api/treatments/')
.set('api-secret', api_secret_hash)
.send(updated)
.end(function () { /* response may never arrive; ignore */ });
// Check the database after the server has had time to process the upsert
setTimeout(function () {
rawCollection().find({ eventType: 'Temporary Override' }).toArray(function (err, docs) {
try {
should.not.exist(err);
docs.length.should.equal(1,
'PUT should update the existing legacy override, not create a duplicate. '
+ 'Found ' + docs.length + ' documents. '
+ 'normalizeTreatmentId promotes UUID to identifier, then upsertQueryFor '
+ 'matches by identifier -- which the legacy doc lacks -- so MongoDB '
+ 'inserts a NEW document instead of updating the original.'
);
done();
} catch (e) {
done(e);
}
});
}, 5000);
});
});
});
describe('GET legacy UUID override via API', function () {
it('GET /api/v1/treatments/?find[_id]=UUID should find the legacy document', function (done) {
insertLegacyDoc(function () {
request(self.app)
.get('/api/treatments/')
.set('api-secret', api_secret_hash)
.query('find[_id]=' + LEGACY_UUID)
.end(function (err, res) {
res.status.should.equal(200,
'GET with UUID _id should return 200, not ' + res.status + '. '
+ 'HTTP 500 means the query layer crashes on UUID _id (issue #6923).'
);
res.body.should.be.an.Array();
res.body.length.should.equal(1,
'GET should find the legacy override when querying by UUID _id. '
+ 'The query rewrite changes {_id: UUID} to {identifier: UUID}, '
+ 'but legacy docs have no identifier field, so 0 results returned.'
);
done();
});
});
});
});
});