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: getPatientInsForDMP in /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: getPatientInsForDMP in /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: envIsProduction in /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: nationalHealthIdentifier in /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.ts in /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: getPatientInsForDMP in /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 getPatientInsForDMP is correctly isolated with jest.mock at the module level, and jest.clearAllMocks() in beforeEach prevents cross-test contamination. - The test "should use empty ins when patient is absent" explicitly asserts getPatientInsForDMP is 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 the as unknown as Patient cast, catching any future accidental rename of ins in the shim object. - getDocumentsContent error 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 component is undefined or structured without cdaContentInBase64. --- 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)