@@ -93,16 +93,19 @@ def test_cytetype_success(
9393 "clusterId" : "1" ,
9494 "annotation" : "Cell Type A" ,
9595 "ontologyTerm" : "CL:0000001" ,
96+ "ontologyTermID" : "CL:0000001" ,
9697 }, # Corresponds to '0'
9798 {
9899 "clusterId" : "2" ,
99100 "annotation" : "Cell Type B" ,
100101 "ontologyTerm" : "CL:0000002" ,
102+ "ontologyTermID" : "CL:0000002" ,
101103 }, # Corresponds to '1'
102104 {
103105 "clusterId" : "3" ,
104106 "annotation" : "Cell Type C" ,
105107 "ontologyTerm" : "CL:0000003" ,
108+ "ontologyTermID" : "CL:0000003" ,
106109 }, # Corresponds to '2'
107110 ]
108111 }
@@ -265,16 +268,19 @@ def test_cytetype_with_auth_token(
265268 "clusterId" : "1" ,
266269 "annotation" : "Cell Type A" ,
267270 "ontologyTerm" : "CL:0000001" ,
271+ "ontologyTermID" : "CL:0000001" ,
268272 },
269273 {
270274 "clusterId" : "2" ,
271275 "annotation" : "Cell Type B" ,
272276 "ontologyTerm" : "CL:0000002" ,
277+ "ontologyTermID" : "CL:0000002" ,
273278 },
274279 {
275280 "clusterId" : "3" ,
276281 "annotation" : "Cell Type C" ,
277282 "ontologyTerm" : "CL:0000003" ,
283+ "ontologyTermID" : "CL:0000003" ,
278284 },
279285 ]
280286 }
@@ -312,6 +318,7 @@ def test_cytetype_get_results_helper(
312318 "clusterId" : "1" ,
313319 "annotation" : "Cell Type A" ,
314320 "ontologyTerm" : "CL:0000001" ,
321+ "ontologyTermID" : "CL:0000001" ,
315322 },
316323 ]
317324 }
@@ -362,6 +369,7 @@ def test_cytetype_with_metadata(
362369 "clusterId" : "1" ,
363370 "annotation" : "Cell Type A" ,
364371 "ontologyTerm" : "CL:0000001" ,
372+ "ontologyTermID" : "CL:0000001" ,
365373 },
366374 ]
367375 }
@@ -407,6 +415,7 @@ def test_cytetype_without_metadata(
407415 "clusterId" : "1" ,
408416 "annotation" : "Cell Type A" ,
409417 "ontologyTerm" : "CL:0000001" ,
418+ "ontologyTermID" : "CL:0000001" ,
410419 },
411420 ]
412421 }
@@ -421,6 +430,113 @@ def test_cytetype_without_metadata(
421430 assert "metadata" not in query_arg
422431
423432
433+ @patch ("cytetype.main.submit_job" )
434+ @patch ("cytetype.main.poll_for_results" )
435+ def test_cytetype_obs_columns (
436+ mock_poll : MagicMock , mock_submit : MagicMock , mock_adata : anndata .AnnData
437+ ) -> None :
438+ """Test that all expected obs columns are created with correct names and values."""
439+ job_id = "mock_job_obs_columns"
440+ mock_submit .return_value = job_id
441+ mock_result : dict [str , list [dict [str , str ]]] = {
442+ "annotations" : [
443+ {
444+ "clusterId" : "1" ,
445+ "annotation" : "T cell" ,
446+ "ontologyTerm" : "T cell" ,
447+ "ontologyTermID" : "CL:0000084" ,
448+ "cellState" : "activated" ,
449+ },
450+ {
451+ "clusterId" : "2" ,
452+ "annotation" : "B cell" ,
453+ "ontologyTerm" : "B cell" ,
454+ "ontologyTermID" : "CL:0000236" ,
455+ "cellState" : "naive" ,
456+ },
457+ {
458+ "clusterId" : "3" ,
459+ "annotation" : "Monocyte" ,
460+ "ontologyTerm" : "monocyte" ,
461+ "ontologyTermID" : "CL:0000576" ,
462+ "cellState" : "" , # Empty cell state
463+ },
464+ ]
465+ }
466+ mock_poll .return_value = mock_result
467+
468+ group_key = "leiden"
469+ results_prefix = "cytetype"
470+
471+ cytetype = CyteType (mock_adata , group_key = group_key )
472+ adata_result = cytetype .run (study_context = "Test study context" )
473+
474+ # Check all expected obs columns exist
475+ expected_columns = [
476+ f"{ results_prefix } _annotation_{ group_key } " ,
477+ f"{ results_prefix } _cellOntologyTerm_{ group_key } " ,
478+ f"{ results_prefix } _cellOntologyTermID_{ group_key } " ,
479+ f"{ results_prefix } _cellState_{ group_key } " ,
480+ ]
481+
482+ for col in expected_columns :
483+ assert col in adata_result .obs , f"Column { col } not found in obs"
484+ assert isinstance (adata_result .obs [col ].dtype , pd .CategoricalDtype ), (
485+ f"Column { col } is not categorical"
486+ )
487+
488+ # Check annotation values are correctly mapped
489+ anno_col = f"{ results_prefix } _annotation_{ group_key } "
490+ ct_map = {"0" : "1" , "1" : "2" , "2" : "3" } # cluster label -> cluster ID mapping
491+ anno_map = {"1" : "T cell" , "2" : "B cell" , "3" : "Monocyte" }
492+ expected_annotations = [
493+ anno_map [ct_map [str (label )]] for label in mock_adata .obs [group_key ]
494+ ]
495+ pd .testing .assert_series_equal (
496+ adata_result .obs [anno_col ],
497+ pd .Series (expected_annotations , index = mock_adata .obs .index , dtype = "category" ),
498+ check_names = False ,
499+ )
500+
501+ # Check ontologyTerm values are correctly mapped
502+ ontology_term_col = f"{ results_prefix } _cellOntologyTerm_{ group_key } "
503+ ontology_term_map = {"1" : "T cell" , "2" : "B cell" , "3" : "monocyte" }
504+ expected_ontology_terms = [
505+ ontology_term_map [ct_map [str (label )]] for label in mock_adata .obs [group_key ]
506+ ]
507+ pd .testing .assert_series_equal (
508+ adata_result .obs [ontology_term_col ],
509+ pd .Series (
510+ expected_ontology_terms , index = mock_adata .obs .index , dtype = "category"
511+ ),
512+ check_names = False ,
513+ )
514+
515+ # Check ontologyTermID values are correctly mapped
516+ ontology_id_col = f"{ results_prefix } _cellOntologyTermID_{ group_key } "
517+ ontology_id_map = {"1" : "CL:0000084" , "2" : "CL:0000236" , "3" : "CL:0000576" }
518+ expected_ontology_ids = [
519+ ontology_id_map [ct_map [str (label )]] for label in mock_adata .obs [group_key ]
520+ ]
521+ pd .testing .assert_series_equal (
522+ adata_result .obs [ontology_id_col ],
523+ pd .Series (expected_ontology_ids , index = mock_adata .obs .index , dtype = "category" ),
524+ check_names = False ,
525+ )
526+
527+ # Check cellState values are correctly mapped (including empty string)
528+ cell_state_col = f"{ results_prefix } _cellState_{ group_key } "
529+ cell_state_map = {"1" : "activated" , "2" : "naive" , "3" : "" }
530+ expected_cell_states = [
531+ cell_state_map [ct_map [str (label )]] for label in mock_adata .obs [group_key ]
532+ ]
533+ pd .testing .assert_series_equal (
534+ adata_result .obs [cell_state_col ],
535+ pd .Series (expected_cell_states , index = mock_adata .obs .index , dtype = "category" ),
536+ check_names = False ,
537+ )
538+
539+
424540# --- TODO ---
425541# - Add tests specifically for cytetype/anndata_helpers.py
426542# - Add tests specifically for cytetype/client.py (e.g., more nuanced API responses)
0 commit comments