Project: workspace-doctolib · Branch: HEAD · Mode: ` · **Model:**claude-sonnet-4-6`
Stats: 1 user messages, 10 tool calls, tools used: Read, Glob, Grep.
Conversation
Turn 1 — User
Analyze test coverage for PR #240093 in the doctolib monolith repo at ~/workspace/doctolib/worktrees/doctolib/fhir-2209_fix-patient-ins-env-transformation.
The PR fixes FHIR-2209: patient INS env transformation in DmpNhrAdapter.toSearchProps. The DMP API requires a T suffix on the patient INS in non-production environments. The fix delegates this to getPatientInsForDMP from @doctolib/dmp.
Changed files (relative to last green master e8751c790841):
- packages/@doctolib/national-health-records/src/business/adapters/DmpNhrAdapter.ts
- packages/@doctolib/national-health-records/src/business/adapters/DmpNhrAdapter.test.ts
Please read both files and assess the quality and completeness of the test coverage. Focus on:
1. Whether all meaningful code paths in DmpNhrAdapter.ts are covered
2. Whether the new getPatientInsForDMP delegation and its guard logic are properly tested
3. Any edge cases or scenarios missing from the test suite
Turn 1 — Assistant
I'll read both files to understand the implementation and test coverage.
Tools used:
Read:/Users/USER/workspace/doctolib/worktrees/doctolib/fhir-2209_fix-patient-ins-env-transformation/packages/@doctolib/national-health-records/src/business/adapters/DmpNhrAdapter.ts
Tool results:
→ result (ok): 1 import type { 2 DmpApi, 3 DmpAuthor, 4 DmpDocument, 5 DmpDocumentContent, 6 SearchProps, 7 SearchResult as DmpSearchResult, 8 } from '@doctolib/dmp' 9 import { DOCUMENT_STATUS, DOCUMENT_VISIBILITY, getPatientInsForDMP } from '@doctolib/dmp' 10 import { isUnstructured } from '@doctolib/cda/types/UnstructuredBody' 11 import { isStructured } from '@doctolib/cda/types/StructuredBody' 12 import type { Patient } from 'monolith_DEPRECATED/types/doctor/api/Patient' 13 import type { 14 Nh …(truncated, 5790 more chars)
Tools used:
Read:/Users/USER/workspace/doctolib/worktrees/doctolib/fhir-2209_fix-patient-ins-env-transformation/packages/@doctolib/national-health-records/src/business/adapters/DmpNhrAdapter.test.ts
Tool results:
→ result (ok): 1 import { getPatientInsForDMP } from '@doctolib/dmp'
2 import type { DmpApi, DmpDocumentContent, SearchResult as DmpSearchResult } from '@doctolib/dmp'
3 import type { NhrDocumentSearchProps } from '@doctolib/tpf-national-health-records'
4 import { DmpNhrAdapter } from './DmpNhrAdapter'
5
6 jest.mock('@doctolib/dmp', () => ({
7 getPatientInsForDMP: jest.fn(),
8 DOCUMENT_STATUS: { validated: 1, obsolete: 2, archived: 4 },
9 DOCUMENT_VISIBILITY: {
10 sharedWithAll: 1,
11 notSharedW
…(truncated, 12555 more chars)
Now let me also look at the getPatientInsForDMP function in the @doctolib/dmp package to understand what it actually does, so I can assess whether the mock captures its contract correctly.
Tools used:
Glob:**/getPatientInsForDMP*in/Users/USER/workspace/doctolib/worktrees/doctolib/fhir-2209_fix-patient-ins-env-transformation
Tools used:
Grep:getPatientInsForDMPin/Users/USER/workspace/doctolib/worktrees/doctolib/fhir-2209_fix-patient-ins-env-transformation/packages/@doctolib
Tool results:
→ result (ok): Found 5 files packages/@doctolib/national-health-records/src/business/adapters/DmpNhrAdapter.test.ts packages/@doctolib/national-health-records/src/business/adapters/DmpNhrAdapter.ts packages/@doctolib/dmp/src/index.ts packages/@doctolib/dmp/src/business/frDmpPayloadBuilders.ts packages/@doctolib/dmp/src/business/FrDmpCommunication.ts
Tool results:
→ result (ok): No files found
Tools used:
Grep:getPatientInsForDMPin/Users/USER/workspace/doctolib/worktrees/doctolib/fhir-2209_fix-patient-ins-env-transformation/packages/@doctolib/dmp/src/business/frDmpPayloadBuilders.ts
Tool results:
→ result (ok): 38- visibility?: number 39- disableMetadataSearch?: number 40- referenceIds?: DmpReferenceId[] 41- eventCodes?: DmpEventCode[] 42- getTransactionFrames?: number 43-} 44- 45-type IcanopeeCdaIdentifier = { 46- s_documentOid?: string 47- s_documentSetIdRoot: string 48- s_documentSetIdExtension?: string 49- s_documentVersionNumber: string 50- s_replacedDocumentUniqueId: string | undefined 51-} 52- 53:export const getPatientInsForDMP = (patient: Patient | undefined): string => { 54- const …(truncated, 4030 more chars)
Tools used:
Grep:envIsProductionin/Users/USER/workspace/doctolib/worktrees/doctolib/fhir-2209_fix-patient-ins-env-transformation/packages/@doctolib/dmp/src/business/frDmpPayloadBuilders.ts
Tool results:
→ result (ok): 1:import { envIsProduction } from '@doctolib/variation-management/environment'
2-import type { Patient } from 'monolith_DEPRECATED/types/doctor/api/Patient'
3-import dayjs from 'dayjs'
4-import type { CdaIdentifierFr } from '@doctolib/tpf-cda'
--
51-}
52-
53-export const getPatientInsForDMP = (patient: Patient | undefined): string => {
54: const insSuffix = envIsProduction() ? '' : 'T'
55- return patient ? ${patient.ins}${insSuffix} : ''
56-}
57-
--
178- Ins: {
179- s_value: pati
…(truncated, 325 more chars)
Tools used:
Grep:nationalHealthIdentifierin/Users/USER/workspace/doctolib/worktrees/doctolib/fhir-2209_fix-patient-ins-env-transformation/packages/@doctolib/national-health-records/src
Tool results:
→ result (ok): packages/@doctolib/national-health-records/src/hooks/useSearchDocuments.test.ts:27: patientInformation: { nationalHealthIdentifier: 'INS-123' }, packages/@doctolib/national-health-records/src/hooks/useSearchDocuments.test.ts:281: patient: { nationalHealthIdentifier: 'INS-123' }, packages/@doctolib/national-health-records/src/business/mappers/toNhrPatientInformation.ts:5: nationalHealthIdentifier: ins, packages/@doctolib/national-health-records/src/business/adapters/DmpNhrAdapter.tes …(truncated, 1262 more chars)
Now I have a complete picture. Let me also check if there's a test for getPatientInsForDMP itself in the @doctolib/dmp package to understand the full testing landscape.
Tools used:
Glob:**/frDmpPayloadBuilders.test*in/Users/USER/workspace/doctolib/worktrees/doctolib/fhir-2209_fix-patient-ins-env-transformation/packages/@doctolib/dmp
Tool results:
→ result (ok): No files found
Tools used:
Glob:**/*.test.tsin/Users/USER/workspace/doctolib/worktrees/doctolib/fhir-2209_fix-patient-ins-env-transformation/packages/@doctolib/dmp/src
Tool results:
→ result (ok): packages/@doctolib/dmp/src/api/connectToDmp.test.ts packages/@doctolib/dmp/src/business/CallDmpDelegate.test.ts packages/@doctolib/dmp/src/business/DmpSharingSettings.test.ts packages/@doctolib/dmp/src/business/FrDmpCommunication.test.ts packages/@doctolib/dmp/src/business/mapCpsToPscPayload.test.ts packages/@doctolib/dmp/src/data/dmpSessionStorage.test.ts packages/@doctolib/dmp/src/hooks/tests/DmpSharingService.deprecated.test.ts packages/@doctolib/dmp/src/hooks/tests/useFrDmpAvailability.test. …(truncated, 177 more chars)
Tools used:
Grep:getPatientInsForDMPin/Users/USER/workspace/doctolib/worktrees/doctolib/fhir-2209_fix-patient-ins-env-transformation/packages/@doctolib/dmp/src
Tool results:
→ result (ok): /Users/USER/workspace/doctolib/worktrees/doctolib/fhir-2209_fix-patient-ins-env-transformation/packages/@doctolib/dmp/src/index.ts-86- getCpxStatus, /Users/USER/workspace/doctolib/worktrees/doctolib/fhir-2209_fix-patient-ins-env-transformation/packages/@doctolib/dmp/src/index.ts-87- getDmpMajorityAge, /Users/USER/workspace/doctolib/worktrees/doctolib/fhir-2209_fix-patient-ins-env-transformation/packages/@doctolib/dmp/src/index.ts-88- getDocumentsContent, packages/@doctolib/dmp/src/index.ts:89 …(truncated, 6863 more chars)
I now have everything needed for a thorough analysis. Here is the assessment.
Summary
The test suite for this PR is solid for its scope. The happy-path delegation to getPatientInsForDMP is verified, the no-patient guard on searchDocuments is covered, and the getDocumentsContent section has comprehensive coverage of all file-extraction branches. However, there are two meaningful gaps and one quality issue worth addressing.
Critical Gaps
1. getDocumentsContent does not apply getPatientInsForDMP - and the tests do not guard against a future regression that would add it incorrectly (Criticality: 7)
The production code at line 165 passes patient.nationalHealthIdentifier directly to getDocumentsContentByUniqueId, with no getPatientInsForDMP wrapping:
ins: patient.nationalHealthIdentifier, // DmpNhrAdapter.ts line 165
The test at line 204-215 asserts ins: 'INS-123' (the bare value), which is correct. But nowhere in the test file is it made explicit that getPatientInsForDMP must NOT be called by getDocumentsContent. If a future developer, noticing the searchDocuments delegation pattern, copies it into getDocumentsContent by mistake, the tests would catch it only because the mock already returns 'INS-123T' and the assertion would fail - but only incidentally, not by design.
The test should add an explicit expect(getPatientInsForDMP).not.toHaveBeenCalled() assertion inside the getDocumentsContent happy-path test to make the contract explicit and self-documenting.
2. Missing test: patient.nationalHealthIdentifier is an empty string (Criticality: 6)
In toSearchProps, the guard is:
ins: patient?.nationalHealthIdentifier
? getPatientInsForDMP(...)
: ''
This is a truthiness check, not a null/undefined check. An empty string '' is falsy, so a patient object present but with an empty nationalHealthIdentifier falls through to '' and getPatientInsForDMP is never called - same as the no-patient case. This is probably the right behavior, but it is undocumented by any test. A test case like:
await adapter.searchDocuments({ patient: { nationalHealthIdentifier: '' }, params: searchProps.params })
// expect getPatientInsForDMP not called
// expect ins: ''
would verify this edge and prevent someone from changing the guard to !== undefined (which would break the non-production suffix logic for empty strings).
Important Improvements
3. The as unknown as Patient cast in toSearchProps is brittle and the test does not assert the exact shape passed to getPatientInsForDMP (Criticality: 5)
The implementation constructs a synthetic Patient object:
getPatientInsForDMP({ ins: patient.nationalHealthIdentifier } as unknown as Patient)
The existing test correctly asserts:
expect(getPatientInsForDMP).toHaveBeenCalledWith({ ins: 'INS-123' })
This is good. However, getPatientInsForDMP accesses patient.ins (not patient.nationalHealthIdentifier) - the cast bridges that field name mismatch. If someone renames the constructed key or changes the cast, the mock would silently receive the wrong argument and getPatientInsForDMP (which in production uses patient.ins) would return an empty/wrong suffix. The existing assertion already catches this correctly, but it is worth noting this architectural mismatch in a code comment, not just a test.
4. No test for the numeric status and visibility paths in toSearchProps (Criticality: 4)
toStatus and toVisibility both have a numeric fast-path:
function toStatus(status: string): number | undefined {
const num = Number(status)
if (status !== '' && !Number.isNaN(num)) return num // ← numeric string branch
return DOCUMENT_STATUS[status as keyof typeof DOCUMENT_STATUS]
}
function toVisibility(visibility: string): number | undefined {
const num = Number(visibility)
if (!Number.isNaN(num)) return num // ← numeric string branch
return DOCUMENT_VISIBILITY[visibility]
}
The existing test only exercises the string-key branch (e.g. 'validated', 'sharedWithAll'). Passing status: '1' or visibility: '2' as numeric strings would exercise the other branch and ensure those paths are not accidentally removed. This is low-criticality because these paths pre-exist the PR's change, but they are currently untested.
Test Quality Issues
5. Two categorization: null tests are redundant (Criticality: 2)
The tests "should return categorization: null for a known typeCode" and "should return categorization: null for an unknown typeCode" both assert first?.categorization is null and differ only in the _code value on the CDA content. Since the implementation always returns null for categorization (per the TODO comment for FHIR-2145), these two tests are testing against a constant. They do not add coverage and will need to be deleted or significantly rewritten when FHIR-2145 lands. They would be better collapsed into one or removed, to avoid giving false confidence about typeCode-to-category mapping.
Positive Observations
- The mock of
getPatientInsForDMPis correctly isolated withjest.mockat the module level, andjest.clearAllMocks()inbeforeEachprevents cross-test contamination. - The test "should use empty ins when patient is absent" explicitly asserts
getPatientInsForDMPis not called - this is exactly the right way to document the guard contract. - The assertion
expect(getPatientInsForDMP).toHaveBeenCalledWith({ ins: 'INS-123' })precisely verifies the field mapping through theas unknown as Patientcast, catching any future accidental rename ofinsin the shim object. getDocumentsContenterror path ("should throw when patient is undefined") is tested, which is important given that branch terminates with a thrown error that callers must handle.- The N1/N3 file-extraction branches are both covered, as is the degenerate case where
componentis undefined or structured withoutcdaContentInBase64.
Files reviewed:
- /Users/USER/workspace/doctolib/worktrees/doctolib/fhir-2209_fix-patient-ins-env-transformation/packages/@doctolib/national-health-records/src/business/adapters/DmpNhrAdapter.ts
- /Users/USER/workspace/doctolib/worktrees/doctolib/fhir-2209_fix-patient-ins-env-transformation/packages/@doctolib/national-health-records/src/business/adapters/DmpNhrAdapter.test.ts
- /Users/USER/workspace/doctolib/worktrees/doctolib/fhir-2209_fix-patient-ins-env-transformation/packages/@doctolib/dmp/src/business/frDmpPayloadBuilders.ts (for getPatientInsForDMP source of truth)