@@ -79,8 +79,8 @@ def test_map_concepts(self, sync_client: OMOPHub, base_url: str) -> None:
7979 )
8080
8181 result = sync_client .mappings .map (
82- source_concepts = [{"concept_id" : 201826 }],
8382 target_vocabulary = "ICD10CM" ,
83+ source_concepts = [201826 ],
8484 )
8585
8686 assert "mappings" in result
@@ -97,18 +97,72 @@ def test_map_concepts_with_options(
9797 )
9898
9999 sync_client .mappings .map (
100- source_concepts = [
101- {"concept_id" : 201826 },
102- {"vocabulary_id" : "SNOMED" , "concept_code" : "44054006" },
103- ],
104100 target_vocabulary = "ICD10CM" ,
101+ source_concepts = [201826 , 4329847 ],
105102 mapping_type = "equivalent" ,
106103 include_invalid = True ,
107104 )
108105
109106 # Verify POST body
110107 assert route .calls [0 ].request .content
111108
109+ @respx .mock
110+ def test_map_concepts_with_source_codes (
111+ self , sync_client : OMOPHub , base_url : str
112+ ) -> None :
113+ """Test mapping concepts using source_codes parameter."""
114+ import json
115+
116+ map_response = {
117+ "success" : True ,
118+ "data" : {
119+ "mappings" : [
120+ {
121+ "source_concept_id" : 4306040 ,
122+ "source_concept_name" : "Acetaminophen" ,
123+ "target_concept_id" : 1125315 ,
124+ "target_vocabulary_id" : "RxNorm" ,
125+ }
126+ ],
127+ },
128+ }
129+ route = respx .post (f"{ base_url } /concepts/map" ).mock (
130+ return_value = Response (200 , json = map_response )
131+ )
132+
133+ result = sync_client .mappings .map (
134+ target_vocabulary = "RxNorm" ,
135+ source_codes = [
136+ {"vocabulary_id" : "SNOMED" , "concept_code" : "387517004" },
137+ {"vocabulary_id" : "SNOMED" , "concept_code" : "108774000" },
138+ ],
139+ )
140+
141+ assert "mappings" in result
142+ # Verify request body contains source_codes
143+ body = json .loads (route .calls [0 ].request .content )
144+ assert "source_codes" in body
145+ assert len (body ["source_codes" ]) == 2
146+ assert body ["source_codes" ][0 ]["vocabulary_id" ] == "SNOMED"
147+
148+ def test_map_concepts_requires_source (self , sync_client : OMOPHub ) -> None :
149+ """Test that map() raises error when neither source_concepts nor source_codes provided."""
150+ with pytest .raises (
151+ ValueError , match = "Either source_concepts or source_codes is required"
152+ ):
153+ sync_client .mappings .map (target_vocabulary = "ICD10CM" )
154+
155+ def test_map_concepts_rejects_both_sources (self , sync_client : OMOPHub ) -> None :
156+ """Test that map() raises error when both source_concepts and source_codes provided."""
157+ with pytest .raises (
158+ ValueError , match = "Cannot use both source_concepts and source_codes"
159+ ):
160+ sync_client .mappings .map (
161+ target_vocabulary = "ICD10CM" ,
162+ source_concepts = [201826 ],
163+ source_codes = [{"vocabulary_id" : "SNOMED" , "concept_code" : "44054006" }],
164+ )
165+
112166
113167class TestAsyncMappingsResource :
114168 """Tests for the asynchronous AsyncMappings resource."""
@@ -157,8 +211,8 @@ async def test_async_map_concepts(
157211 )
158212
159213 result = await async_client .mappings .map (
160- source_concepts = [{"concept_id" : 201826 }],
161214 target_vocabulary = "ICD10CM" ,
215+ source_concepts = [201826 ],
162216 )
163217
164218 assert "mappings" in result
@@ -174,10 +228,57 @@ async def test_async_map_concepts_with_options(
174228 )
175229
176230 await async_client .mappings .map (
177- source_concepts = [{"concept_id" : 201826 }],
178231 target_vocabulary = "ICD10CM" ,
232+ source_concepts = [201826 ],
179233 mapping_type = "direct" ,
180234 include_invalid = True ,
181235 )
182236
183237 assert route .calls [0 ].request .content
238+
239+ @pytest .mark .asyncio
240+ @respx .mock
241+ async def test_async_map_concepts_with_source_codes (
242+ self , async_client : omophub .AsyncOMOPHub , base_url : str
243+ ) -> None :
244+ """Test async mapping concepts using source_codes."""
245+ import json
246+
247+ route = respx .post (f"{ base_url } /concepts/map" ).mock (
248+ return_value = Response (200 , json = {"success" : True , "data" : {"mappings" : []}})
249+ )
250+
251+ result = await async_client .mappings .map (
252+ target_vocabulary = "RxNorm" ,
253+ source_codes = [
254+ {"vocabulary_id" : "SNOMED" , "concept_code" : "387517004" },
255+ ],
256+ )
257+
258+ assert "mappings" in result
259+ body = json .loads (route .calls [0 ].request .content )
260+ assert "source_codes" in body
261+
262+ @pytest .mark .asyncio
263+ async def test_async_map_requires_source (
264+ self , async_client : omophub .AsyncOMOPHub
265+ ) -> None :
266+ """Test async map() raises error without sources."""
267+ with pytest .raises (
268+ ValueError , match = "Either source_concepts or source_codes is required"
269+ ):
270+ await async_client .mappings .map (target_vocabulary = "ICD10CM" )
271+
272+ @pytest .mark .asyncio
273+ async def test_async_map_rejects_both_sources (
274+ self , async_client : omophub .AsyncOMOPHub
275+ ) -> None :
276+ """Test async map() raises error with both sources."""
277+ with pytest .raises (
278+ ValueError , match = "Cannot use both source_concepts and source_codes"
279+ ):
280+ await async_client .mappings .map (
281+ target_vocabulary = "ICD10CM" ,
282+ source_concepts = [201826 ],
283+ source_codes = [{"vocabulary_id" : "SNOMED" , "concept_code" : "44054006" }],
284+ )
0 commit comments