1import unittest 2import json 3import fhirspec_pb2 4from fhir_spec_extractor import FhirSpecExtractor 5from google.protobuf import text_format 6 7 8class FhirSpecExtractorTest(unittest.TestCase): 9 BUNDLE_WITH_IMMUNIZATION_AND_PATIENT_STRUCTURE_DEFINITION = json.loads(""" 10 { 11 "resourceType" : "Bundle", 12 "id" : "resources", 13 "meta" : { 14 "lastUpdated" : "2019-11-01T09:29:23.356+11:00" 15 }, 16 "type" : "collection", 17 "entry" : [ 18 { 19 "fullUrl" : "http://hl7.org/fhir/CompartmentDefinition/relatedPerson" 20 }, 21 { 22 "fullUrl" : "http://hl7.org/fhir/StructureDefinition/Immunization", 23 "resource" : { 24 "resourceType" : "StructureDefinition", 25 "id" : "Immunization", 26 "meta" : { 27 "lastUpdated" : "2019-11-01T09:29:23.356+11:00" 28 }, 29 "fhirVersion" : "4.0.1", 30 "kind" : "resource", 31 "type" : "Immunization", 32 "baseDefinition" : "http://hl7.org/fhir/StructureDefinition/DomainResource", 33 "snapshot" : { 34 "element" : [{ 35 "id" : "Immunization", 36 "path" : "Immunization", 37 "min" : 0, 38 "max" : "*", 39 "base" : { 40 "path" : "Immunization", 41 "min" : 0, 42 "max" : "*" 43 } 44 }, 45 { 46 "id" : "Immunization.id", 47 "path" : "Immunization.id", 48 "min" : 0, 49 "max" : "1", 50 "base" : { 51 "path" : "Resource.id", 52 "min" : 0, 53 "max" : "1" 54 }, 55 "type" : [{ 56 "extension" : [{ 57 "url" : "http://hl7.org/fhir/StructureDefinition/structuredefinition-fhir-type", 58 "valueUrl" : "string" 59 }], 60 "code" : "http://hl7.org/fhirpath/System.String" 61 }] 62 }, 63 { 64 "id" : "Immunization.status", 65 "path" : "Immunization.status", 66 "min" : 1, 67 "max" : "1", 68 "base" : { 69 "path" : "Immunization.status", 70 "min" : 1, 71 "max" : "1" 72 }, 73 "type" : [{ 74 "code" : "code" 75 }] 76 }, 77 { 78 "id" : "Immunization.vaccineCode", 79 "path" : "Immunization.vaccineCode", 80 "min" : 1, 81 "max" : "1", 82 "base" : { 83 "path" : "Immunization.vaccineCode", 84 "min" : 1, 85 "max" : "1" 86 }, 87 "type" : [{ 88 "code" : "CodeableConcept" 89 }] 90 }, 91 { 92 "id" : "Immunization.exampleFieldToTestOneToMany", 93 "path" : "Immunization.exampleFieldToTestOneToMany", 94 "min" : 1, 95 "max" : "*", 96 "base" : { 97 "path" : "Immunization.exampleFieldToTestOneToMany", 98 "min" : 1, 99 "max" : "*" 100 }, 101 "type" : [{ 102 "code" : "CodeableConcept" 103 }] 104 }, 105 { 106 "id" : "Immunization.occurrence[x]", 107 "path" : "Immunization.occurrence[x]", 108 "min" : 1, 109 "max" : "1", 110 "base" : { 111 "path" : "Immunization.occurrence[x]", 112 "min" : 1, 113 "max" : "1" 114 }, 115 "type" : [{ 116 "code" : "dateTime" 117 }, 118 { 119 "code" : "string" 120 }] 121 }, 122 { 123 "id" : "Immunization.performer", 124 "path" : "Immunization.performer", 125 "min" : 0, 126 "max" : "*", 127 "base" : { 128 "path" : "Immunization.performer", 129 "min" : 0, 130 "max" : "*" 131 }, 132 "type" : [{ 133 "code" : "BackboneElement" 134 }] 135 }, 136 { 137 "id" : "Immunization.performer.id", 138 "path" : "Immunization.performer.id", 139 "min" : 0, 140 "max" : "1", 141 "base" : { 142 "path" : "Element.id", 143 "min" : 0, 144 "max" : "1" 145 }, 146 "type" : [{ 147 "extension" : [{ 148 "url" : "http://hl7.org/fhir/StructureDefinition/structuredefinition-fhir-type", 149 "valueUrl" : "string" 150 }], 151 "code" : "http://hl7.org/fhirpath/System.String" 152 }] 153 }, 154 { 155 "id" : "Immunization.performer.extension", 156 "path" : "Immunization.performer.extension", 157 "min" : 0, 158 "max" : "*", 159 "base" : { 160 "path" : "Element.extension", 161 "min" : 0, 162 "max" : "*" 163 }, 164 "type" : [{ 165 "code" : "Extension" 166 }] 167 } 168 ] 169 } 170 } 171 }, 172 { 173 "fullUrl" : "http://hl7.org/fhir/StructureDefinition/Patient", 174 "resource" : { 175 "resourceType" : "StructureDefinition", 176 "id" : "Patient", 177 "meta" : { 178 "lastUpdated" : "2019-11-01T09:29:23.356+11:00" 179 }, 180 "url" : "http://hl7.org/fhir/StructureDefinition/Patient", 181 "fhirVersion" : "4.0.1", 182 "kind" : "resource", 183 "type" : "Patient", 184 "snapshot" : { 185 "element" : [{ 186 "id" : "Patient.id", 187 "path" : "Patient.id", 188 "min" : 0, 189 "max" : "1", 190 "base" : { 191 "path" : "Resource.id", 192 "min" : 0, 193 "max" : "1" 194 }, 195 "type" : [{ 196 "extension" : [{ 197 "url" : "http://hl7.org/fhir/StructureDefinition/structuredefinition-fhir-type", 198 "valueUrl" : "string" 199 }], 200 "code" : "http://hl7.org/fhirpath/System.String" 201 }] 202 }] 203 } 204 } 205 } 206 ] 207 } 208 """) 209 210 IMMUNIZATION_RESOURCE_TYPE_INT = 1 211 212 PATIENT_RESOURCE_TYPE_INT = 9 213 214 def test_fhir_spec_extractor_immunization_resource_produces_expected(self): 215 fhir_spec_extractor = FhirSpecExtractor( 216 self.BUNDLE_WITH_IMMUNIZATION_AND_PATIENT_STRUCTURE_DEFINITION, 217 {"Immunization"}) 218 # we expect each top level field to be present, and one ofs that are represented in the spec 219 # as e.g. occurrence[x] should be expanded into the individual fields such as 220 # occurrenceDateTime and occurrenceString. Fields with a cardinality of 0..* or 1..* should 221 # have is_array = true set in their config. 222 expected_required_fields = {"status", "vaccineCode", "exampleFieldToTestOneToMany"} 223 expected_multi_type_config = fhirspec_pb2.MultiTypeFieldConfig( 224 name="occurrence[x]", 225 typed_field_names=["occurrenceDateTime", "occurrenceString"], 226 is_required=True 227 ) 228 expected_field_names_to_config = { 229 "resourceType": fhirspec_pb2.FhirFieldConfig( 230 is_array=False, 231 r4_type=fhirspec_pb2.R4FhirType.R4_FHIR_TYPE_STRING, 232 kind=fhirspec_pb2.Kind.KIND_PRIMITIVE_TYPE 233 ), 234 "id": fhirspec_pb2.FhirFieldConfig( 235 is_array=False, 236 r4_type=fhirspec_pb2.R4FhirType.R4_FHIR_TYPE_SYSTEM_STRING, 237 kind=fhirspec_pb2.Kind.KIND_PRIMITIVE_TYPE 238 ), 239 "status": fhirspec_pb2.FhirFieldConfig( 240 is_array=False, 241 r4_type=fhirspec_pb2.R4FhirType.R4_FHIR_TYPE_CODE, 242 kind=fhirspec_pb2.Kind.KIND_PRIMITIVE_TYPE 243 ), 244 "vaccineCode": fhirspec_pb2.FhirFieldConfig( 245 is_array=False, 246 r4_type=fhirspec_pb2.R4FhirType.R4_FHIR_TYPE_COMPLEX, 247 kind=fhirspec_pb2.Kind.KIND_COMPLEX_TYPE 248 ), 249 "exampleFieldToTestOneToMany": fhirspec_pb2.FhirFieldConfig( 250 is_array=True, 251 r4_type=fhirspec_pb2.R4FhirType.R4_FHIR_TYPE_COMPLEX, 252 kind=fhirspec_pb2.Kind.KIND_COMPLEX_TYPE 253 ), 254 "occurrenceDateTime": fhirspec_pb2.FhirFieldConfig( 255 is_array=False, 256 r4_type=fhirspec_pb2.R4FhirType.R4_FHIR_TYPE_DATE_TIME, 257 kind=fhirspec_pb2.Kind.KIND_PRIMITIVE_TYPE 258 ), 259 "occurrenceString": fhirspec_pb2.FhirFieldConfig( 260 is_array=False, 261 r4_type=fhirspec_pb2.R4FhirType.R4_FHIR_TYPE_STRING, 262 kind=fhirspec_pb2.Kind.KIND_PRIMITIVE_TYPE 263 ), 264 "performer": fhirspec_pb2.FhirFieldConfig( 265 is_array=True, 266 r4_type=fhirspec_pb2.R4FhirType.R4_FHIR_TYPE_COMPLEX, 267 kind=fhirspec_pb2.Kind.KIND_COMPLEX_TYPE 268 ), 269 } 270 271 generated_spec = fhir_spec_extractor.generate_r4_fhir_spec_proto_message() 272 273 # Check that exactly one Immunization config is present 274 self.assertEqual(len(generated_spec.resource_type_to_config.keys()), 1) 275 immunization_config = ( 276 generated_spec.resource_type_to_config[self.IMMUNIZATION_RESOURCE_TYPE_INT]) 277 # Check that the list of required fields is as expected 278 self.assertEquals(set(immunization_config.required_fields), expected_required_fields) 279 # Check that the list of multi type configs is as expected 280 self.assertEquals(len(immunization_config.multi_type_fields), 1) 281 received_multi_type_config = immunization_config.multi_type_fields[0] 282 self.assertEqual(received_multi_type_config.name, expected_multi_type_config.name) 283 self.assertEqual(received_multi_type_config.typed_field_names, 284 expected_multi_type_config.typed_field_names) 285 self.assertEqual(received_multi_type_config.is_required, 286 expected_multi_type_config.is_required) 287 # Check that the field names to config map is as expected 288 self.assertEqual(set(expected_field_names_to_config.keys()), 289 set(immunization_config.allowed_field_names_to_config.keys())) 290 for expected_field, expected_config in expected_field_names_to_config.items(): 291 self.assertEqual( 292 immunization_config.allowed_field_names_to_config[expected_field], 293 expected_config, 294 "Mismatching config for field " + expected_field 295 ) 296 297 def test_fhir_spec_extractor_immunization_and_patient_contains_two_entries(self): 298 fhir_spec_extractor = FhirSpecExtractor( 299 self.BUNDLE_WITH_IMMUNIZATION_AND_PATIENT_STRUCTURE_DEFINITION, 300 {"Immunization", "Patient"}) 301 302 generated_spec = fhir_spec_extractor.generate_r4_fhir_spec_proto_message() 303 304 self.assertEqual(len(generated_spec.resource_type_to_config), 2) 305 306 def test_fhir_spec_extractor_unsupported_resource_raises_exception(self): 307 with self.assertRaises(ValueError): 308 FhirSpecExtractor( 309 self.BUNDLE_WITH_IMMUNIZATION_AND_PATIENT_STRUCTURE_DEFINITION, 310 {"UnsupportedResource"}) 311 312 def test_fhir_spec_extractor_missing_resource_raises_exception(self): 313 with self.assertRaises(ValueError): 314 FhirSpecExtractor( 315 self.BUNDLE_WITH_IMMUNIZATION_AND_PATIENT_STRUCTURE_DEFINITION, 316 {"Observation"}) 317 318 319if __name__ == '__main__': 320 unittest.main() 321