Repository navigation
Gateway sync marks a completed assignment COMPLETE without importing it when completedAt or symmetricKey is missing #1637
Description
When the gateway reports a completed remote assignment that has encryptedData but a null completedAt or symmetricKey, GatewaySynchronizer.handleAssignmentComplete logs an error and returns normally. sync() treats that as success and calls assignmentsService.updateStatusById(id, 'COMPLETE'). The local assignment is then shown to clinicians as complete, but no session or instrument record was created, and the remote assignment, which still holds the patient's encrypted responses, is never deleted or imported. Each later pass repeats the same log-and-mark-complete cycle. A missing encryptedData is handled differently: it throws, so the status stays untouched and the assignment is retried, which is what the comment in sync() describes. The current gateway writes all three fields together, so reaching this state needs a gateway defect, a version mismatch or edited gateway data. But when it is reached, the result is a submission that looks imported and is not.
Where
apps/api/src/gateway/gateway.synchronizer.ts:110-116:
if (!remoteAssignment.completedAt) {
this.loggingService.error(`Field 'completedAt' is null for assignment '${assignment.id}'`);
return;
} else if (!remoteAssignment.symmetricKey) {
this.loggingService.error(`Field 'symmetricKey' is null for assignment '${assignment.id}'`);
return;
}This is followed by apps/api/src/gateway/gateway.synchronizer.ts:89-94:
} else if (assignment.status === 'COMPLETE') {
await this.handleAssignmentComplete(assignment);
} ...
await this.assignmentsService.updateStatusById(assignment.id, assignment.status);Compare apps/api/src/gateway/gateway.synchronizer.ts:105-107, which throws for missing encryptedData.
Reproduce
Have gatewayService.fetchRemoteAssignments return a remote assignment with status: 'COMPLETE', valid encryptedData, and completedAt: null (or symmetricKey: null). Then run sync().
- Actual: an error is logged,
sessionsService.createis not called,gatewayService.deleteRemoteAssignmentis not called, andassignmentsService.updateStatusById(id, 'COMPLETE')is called. - Expected:
updateStatusByIdis not called and the error is logged as a failure to synchronize that assignment. The local status staysOUTSTANDING, so the problem stays visible and is retried, matching the missing-encryptedDatacase.
Tests
apps/api/src/gateway/__tests__/gateway.synchronizer.spec.ts, describe('incomplete submission'), tests 'should create no session for a submission without a completion date' and 'should create no session for a submission without a symmetric key'. Neither one asserts on updateStatusById, so they do not pin the defect. When fixing:
- add
expect(assignmentsService.updateStatusById).not.toHaveBeenCalled()to both; - change the logged-error assertion to the
{ error: 'Failed to synchronize remote assignment with ID: assignment-1' }shape, as in the siblingencryptedData: nulltest, if the fix throws instead of logging.
Suggested fix
Throw instead of logging and returning, as the encryptedData check already does, and move both checks above the findById call so all three missing-field cases are handled in one place.
Found while raising unit test coverage in #1632.
Activity