From 149d84b5bc543043605907c75bdbcbe8758a24e9 Mon Sep 17 00:00:00 2001 From: jdisho Date: Tue, 31 Dec 2024 18:16:46 +0100 Subject: [PATCH 1/8] Write tests for `FHIRResource` --- Tests/SpeziFHIRTests/FHIRResourceTests.swift | 505 +++++++++++++++++++ 1 file changed, 505 insertions(+) create mode 100644 Tests/SpeziFHIRTests/FHIRResourceTests.swift diff --git a/Tests/SpeziFHIRTests/FHIRResourceTests.swift b/Tests/SpeziFHIRTests/FHIRResourceTests.swift new file mode 100644 index 0000000..3c2879a --- /dev/null +++ b/Tests/SpeziFHIRTests/FHIRResourceTests.swift @@ -0,0 +1,505 @@ +// +// This source file is part of the Stanford Spezi open-source project +// +// SPDX-FileCopyrightText: 2024 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import ModelsR4 +import ModelsDSTU2 +import SpeziFHIR +import XCTest + +final class FHIRResourceTests: XCTestCase { + + let testDate = Date(timeIntervalSince1970: 1735123266) + let calendar = Calendar.current + + func testModelsR4ResourceInitialization() throws { + let mockObservation = try ModelsR4Mocks.createObservation(date: .now) + + let resource = FHIRResource( + resource: mockObservation, + displayName: "Test Observation" + ) + + XCTAssertEqual(resource.id, "observation-id") + XCTAssertEqual(resource.displayName, "Test Observation") + XCTAssertEqual(resource.resourceType, "Observation") + } + + func testModelsDSTU2ResourceInitialization() throws { + let mockObservation = try ModelsDSTU2Mocks.createObservation(date: testDate) + + let resource = FHIRResource( + resource: mockObservation, + displayName: "Test Observation" + ) + + XCTAssertEqual(resource.id, "observation-id") + XCTAssertEqual(resource.displayName, "Test Observation") + XCTAssertEqual(resource.resourceType, "Observation") + } + + func testModelsR4ResourceDates() throws { + let modelsR4Resources: [(ModelsR4.Resource, String)] = try [ + (ModelsR4Mocks.createCarePlan(date: testDate), "CarePlan"), + (ModelsR4Mocks.createCareTeam(date: testDate), "CareTeam"), + (ModelsR4Mocks.createClaim(date: testDate), "Claim"), + (ModelsR4Mocks.createCondition(date: testDate), "Condition"), + (ModelsR4Mocks.createDevice(date: testDate), "Device"), + (ModelsR4Mocks.createDiagnosticReport(date: testDate), "DiagnosticReport"), + (ModelsR4Mocks.createDocumentReference(date: testDate), "DocumentReference"), + (ModelsR4Mocks.createEncounter(date: testDate), "Encounter"), + (ModelsR4Mocks.createExplanationOfBenefit(date: testDate), "ExplanationOfBenefit"), + (ModelsR4Mocks.createImmunization(date: testDate), "Immunization"), + (ModelsR4Mocks.createMedicationRequest(date: testDate), "MedicationRequest"), + (ModelsR4Mocks.createMedicationAdministration(date: testDate), "MedicationAdministration"), + (ModelsR4Mocks.createObservation(date: testDate), "Observation"), + (ModelsR4Mocks.createProcedure(date: testDate), "Procedure"), + (ModelsR4Mocks.createPatient(date: testDate), "Patient"), + (ModelsR4Mocks.createProvenance(date: testDate), "Provenance"), + (ModelsR4Mocks.createSupplyDelivery(date: testDate), "SupplyDelivery") + ] + + for (resource, name) in modelsR4Resources { + let fhirResource = FHIRResource( + versionedResource: .r4(resource), + displayName: "Test \(name)" + ) + + assertEqualDates(fhirResource.date, testDate, name) + } + } + + func testModelsDSTU2ResourceDates() throws { + let modelsDSTU2Resources: [(ModelsDSTU2.Resource, String)] = try [ + (ModelsDSTU2Mocks.createObservation(date: testDate), "Observation"), + (ModelsDSTU2Mocks.createMedicationOrder(date: testDate), "MedicationOrder"), + (ModelsDSTU2Mocks.createMedicationStatement(date: testDate), "MedicationStatement"), + (ModelsDSTU2Mocks.createCondition(date: testDate), "Condition"), + (ModelsDSTU2Mocks.createProcedure(date: testDate), "Procedure"), + (ModelsDSTU2Mocks.createProcedure(date: testDate, usePeriod: false), "Procedure"), + (ModelsDSTU2Mocks.createProcedure(date: testDate, usePeriod: true), "Procedure with Period") + ] + + for (resource, name) in modelsDSTU2Resources { + let fhirResource = FHIRResource( + versionedResource: .dstu2(resource), + displayName: "Test \(name)" + ) + + XCTAssertEqual( + fhirResource.date?.timeIntervalSince1970, + testDate.timeIntervalSince1970, + "Date extraction failed for DSTU2 \(name)" + ) + } + } + + func testModelsR4JSON() throws { + let observation = ModelsR4.Observation( + code: CodeableConcept( + coding: [ + Coding(code: "test-code".asFHIRStringPrimitive()) + ] + ), + id: "test-id".asFHIRStringPrimitive(), + status: FHIRPrimitive(.final) + ) + + let resource = FHIRResource( + versionedResource: .r4(observation), + displayName: "Test" + ) + + let jsonString = resource.json(withConfiguration: []) + + let jsonData = jsonString.data(using: .utf8)! + let decoder = JSONDecoder() + let decodedObservation = try decoder.decode(ModelsR4.Observation.self, from: jsonData) + + XCTAssertEqual(decodedObservation.id, observation.id) + XCTAssertEqual(decodedObservation.code, observation.code) + XCTAssertEqual(decodedObservation.status, observation.status) + } + + func testModelsDSTU2JSON() throws { + let observation = ModelsDSTU2.Observation( + code: CodeableConcept( + coding: [ + Coding(code: "test-code".asFHIRStringPrimitive()) + ] + ), + id: "test-id".asFHIRStringPrimitive(), + status: FHIRPrimitive(.final) + ) + + let resource = FHIRResource( + versionedResource: .dstu2(observation), + displayName: "Test" + ) + + let jsonString = resource.json(withConfiguration: []) + + let jsonData = jsonString.data(using: .utf8)! + let decoder = JSONDecoder() + let decodedObservation = try decoder.decode(ModelsDSTU2.Observation.self, from: jsonData) + + XCTAssertEqual(decodedObservation.id, observation.id) + XCTAssertEqual(decodedObservation.code, observation.code) + XCTAssertEqual(decodedObservation.status, observation.status) + } +} + +private extension FHIRResourceTests { + func assertEqualDates(_ resourceDate: Date?, _ expectedDate: Date, _ resourceName: String) { + guard let resourceDate = resourceDate else { + XCTFail("Date should not be nil for \(resourceName)") + return + } + + // For resources that use FHIRDate (like Patient birthDate), compare only date components + if resourceName == "Patient" { + let testComponents = calendar.dateComponents([.year, .month, .day], from: expectedDate) + let resourceComponents = calendar.dateComponents([.year, .month, .day], from: resourceDate) + + XCTAssertEqual( + testComponents.year, + resourceComponents.year, + "Year mismatch for \(resourceName)" + ) + XCTAssertEqual( + testComponents.month, + resourceComponents.month, + "Month mismatch for \(resourceName)" + ) + XCTAssertEqual( + testComponents.day, + resourceComponents.day, + "Day mismatch for \(resourceName)" + ) + } else { + // For other resources using DateTime/Instant, compare exact timestamps + XCTAssertEqual( + resourceDate.timeIntervalSince1970, + expectedDate.timeIntervalSince1970, + "Date extraction failed for \(resourceName)" + ) + } + } +} + +private struct ModelsR4Mocks { + static func createCarePlan(date: Date) throws -> ModelsR4.CarePlan { + let period = ModelsR4.Period() + period.start = try FHIRPrimitive(DateTime(date: date)) + + return ModelsR4.CarePlan( + id: "care-plan-id".asFHIRStringPrimitive(), + intent: FHIRPrimitive(.plan), + period: period, + status: FHIRPrimitive(.active), + subject: Reference(id: "patient-id".asFHIRStringPrimitive()) + ) + } + + static func createCareTeam(date: Date) throws -> ModelsR4.CareTeam { + let period = ModelsR4.Period() + period.start = try FHIRPrimitive(DateTime(date: date)) + + return ModelsR4.CareTeam( + id: "care-team-id".asFHIRStringPrimitive(), + period: period, + status: FHIRPrimitive(.active) + ) + } + + static func createClaim(date: Date) throws -> ModelsR4.Claim { + let period = ModelsR4.Period() + period.end = try FHIRPrimitive(DateTime(date: date)) + + return ModelsR4.Claim( + billablePeriod: period, + created: FHIRPrimitive(try DateTime(date: .now)), + id: "claim-id".asFHIRStringPrimitive(), + insurance: [ + ClaimInsurance( + coverage: Reference(id: "coverage-id".asFHIRStringPrimitive()), + focal: FHIRPrimitive(true), + sequence: FHIRPrimitive(1) + ) + ], + patient: Reference(id: "patient-id".asFHIRStringPrimitive()), + priority: CodeableConcept( + coding: [ + Coding(code: "normal".asFHIRStringPrimitive()) + ] + ), + provider: Reference(id: "provider-id".asFHIRStringPrimitive()), + status: FHIRPrimitive(.active), + type: CodeableConcept( + coding: [ + Coding(code: "type".asFHIRStringPrimitive()) + ] + ), + use: FHIRPrimitive(.claim) + ) + } + + static func createCondition(date: Date) throws -> ModelsR4.Condition { + return ModelsR4.Condition( + id: "condition-id".asFHIRStringPrimitive(), + onset: .dateTime(FHIRPrimitive(try DateTime(date: date))), + subject: Reference(id: "patient-id".asFHIRStringPrimitive()) + ) + } + + static func createDevice(date: Date) throws -> ModelsR4.Device { + return ModelsR4.Device( + id: "device-id".asFHIRStringPrimitive(), + manufactureDate: FHIRPrimitive(try DateTime(date: date)) + ) + } + + static func createDiagnosticReport(date: Date) throws -> ModelsR4.DiagnosticReport { + return ModelsR4.DiagnosticReport( + code: CodeableConcept(coding: [Coding(code: "code".asFHIRStringPrimitive())]), + effective: .dateTime(FHIRPrimitive(try DateTime(date: date))), + id: "diagnostic-report-id".asFHIRStringPrimitive(), + status: FHIRPrimitive(.final) + ) + } + + static func createDocumentReference(date: Date) throws -> ModelsR4.DocumentReference { + let content = ModelsR4.DocumentReferenceContent( + attachment: Attachment( + contentType: "text/plain".asFHIRStringPrimitive() + ) + ) + + return ModelsR4.DocumentReference( + content: [content], + date: FHIRPrimitive(try Instant(date: date)), + id: "document-reference-id".asFHIRStringPrimitive(), + status: FHIRPrimitive(.current) + ) + } + + static func createEncounter(date: Date) throws -> ModelsR4.Encounter { + let period = ModelsR4.Period() + period.end = try FHIRPrimitive(DateTime(date: date)) + + return ModelsR4.Encounter( + class: Coding( + code: "AMB".asFHIRStringPrimitive(), + system: FHIRPrimitive("http://terminology.hl7.org/CodeSystem/v3-ActCode") + ), + id: "encounter-id".asFHIRStringPrimitive(), + period: period, + status: FHIRPrimitive(.finished) + ) + } + + static func createExplanationOfBenefit(date: Date) throws -> ModelsR4.ExplanationOfBenefit { + let period = ModelsR4.Period() + period.end = try FHIRPrimitive(DateTime(date: date)) + + return ModelsR4.ExplanationOfBenefit( + billablePeriod: period, + created: FHIRPrimitive(try DateTime(date: .now)), + id: "explanation-of-benefit-id".asFHIRStringPrimitive(), + insurance: [ + ExplanationOfBenefitInsurance( + coverage: Reference(id: "coverage-id".asFHIRStringPrimitive()), + focal: FHIRPrimitive(true) + ) + ], + insurer: Reference(id: "insurer-id".asFHIRStringPrimitive()), + outcome: FHIRPrimitive(.complete), + patient: Reference(id: "patient-id".asFHIRStringPrimitive()), + provider: Reference(id: "provider-id".asFHIRStringPrimitive()), + status: FHIRPrimitive(.active), + type: CodeableConcept( + coding: [ + Coding(code: "type".asFHIRStringPrimitive()) + ] + ), + use: FHIRPrimitive(.claim) + ) + } + + static func createImmunization(date: Date) throws -> ModelsR4.Immunization { + return ModelsR4.Immunization( + id: "immunization-id".asFHIRStringPrimitive(), + occurrence: .dateTime(FHIRPrimitive(try DateTime(date: date))), + patient: Reference(id: "patient-id".asFHIRStringPrimitive()), + status: FHIRPrimitive(.completed), + vaccineCode: CodeableConcept( + coding: [ + Coding(code: "vaccine-code".asFHIRStringPrimitive()) + ] + ) + ) + } + + static func createMedicationRequest(date: Date) throws -> ModelsR4.MedicationRequest { + return ModelsR4.MedicationRequest( + authoredOn: FHIRPrimitive(try DateTime(date: date)), + id: "medication-request-id".asFHIRStringPrimitive(), + intent: FHIRPrimitive(.order), + medication: .codeableConcept( + CodeableConcept(coding: [ + Coding(code: "med-code".asFHIRStringPrimitive()) + ]) + ), + status: FHIRPrimitive(.active), + subject: Reference(id: "patient-id".asFHIRStringPrimitive()) + ) + } + + static func createMedicationAdministration(date: Date) throws -> ModelsR4.MedicationAdministration { + return ModelsR4.MedicationAdministration( + effective: .dateTime(FHIRPrimitive(try DateTime(date: date))), + id: "medication-administration-id".asFHIRStringPrimitive(), + medication: .codeableConcept( + CodeableConcept(coding: [ + Coding(code: "med-code".asFHIRStringPrimitive()) + ]) + ), + status: FHIRPrimitive(.completed), + subject: Reference(id: "patient-id".asFHIRStringPrimitive()) + ) + } + + static func createObservation(date: Date) throws -> ModelsR4.Observation { + return ModelsR4.Observation( + code: CodeableConcept(coding: [Coding(code: "code".asFHIRStringPrimitive())]), + id: "observation-id".asFHIRStringPrimitive(), + issued: FHIRPrimitive(try Instant(date: date)), + status: FHIRPrimitive(.final) + ) + } + + static func createProcedure(date: Date) throws -> ModelsR4.Procedure { + return ModelsR4.Procedure( + id: "procedure-id".asFHIRStringPrimitive(), + performed: .dateTime(FHIRPrimitive(try DateTime(date: date))), + status: FHIRPrimitive(.completed), + subject: Reference(id: "patient-id".asFHIRStringPrimitive()) + ) + } + + static func createPatient(date: Date) throws -> ModelsR4.Patient { + return ModelsR4.Patient( + birthDate: FHIRPrimitive(try FHIRDate(date: date)), + id: "patient-id".asFHIRStringPrimitive() + ) + } + + static func createProvenance(date: Date) throws -> ModelsR4.Provenance { + return ModelsR4.Provenance( + agent: [ + ProvenanceAgent( + type: CodeableConcept( + coding: [Coding(code: "agent-type".asFHIRStringPrimitive())] + ), + who: Reference(id: "agent-id".asFHIRStringPrimitive()) + ) + ], + id: "provenance-id".asFHIRStringPrimitive(), + recorded: FHIRPrimitive(try Instant(date: date)), + target: [ + Reference(id: "target-id".asFHIRStringPrimitive()) + ] + ) + } + + static func createSupplyDelivery(date: Date) throws -> ModelsR4.SupplyDelivery { + return ModelsR4.SupplyDelivery( + id: "supply-delivery-id".asFHIRStringPrimitive(), + occurrence: .dateTime(FHIRPrimitive(try DateTime(date: date))), + status: FHIRPrimitive(.completed) + ) + } +} + + +private struct ModelsDSTU2Mocks { + static func createObservation(date: Date) throws -> ModelsDSTU2.Observation { + return ModelsDSTU2.Observation( + code: CodeableConcept( + coding: [ + Coding(code: "code".asFHIRStringPrimitive()) + ] + ), + id: "observation-id".asFHIRStringPrimitive(), + issued: FHIRPrimitive(try Instant(date: date)), + status: FHIRPrimitive(.final) + ) + } + + static func createMedicationOrder(date: Date) throws -> ModelsDSTU2.MedicationOrder { + return ModelsDSTU2.MedicationOrder( + dateWritten: FHIRPrimitive(try DateTime(date: date)), + id: "medication-order-id".asFHIRStringPrimitive(), + medication: .codeableConcept( + CodeableConcept(coding: [ + Coding(code: "med-code".asFHIRStringPrimitive()) + ]) + ), + status: FHIRPrimitive(.active) + ) + } + + static func createMedicationStatement(date: Date) throws -> ModelsDSTU2.MedicationStatement { + return ModelsDSTU2.MedicationStatement( + effective: .dateTime(FHIRPrimitive(try DateTime(date: date))), + id: "medication-statement-id".asFHIRStringPrimitive(), + medication: .codeableConcept( + CodeableConcept(coding: [ + Coding(code: "med-code".asFHIRStringPrimitive()) + ]) + ), + patient: Reference(id: "patient-id".asFHIRStringPrimitive()), + status: FHIRPrimitive(.active) + ) + } + + static func createCondition(date: Date) throws -> ModelsDSTU2.Condition { + return ModelsDSTU2.Condition( + code: CodeableConcept( + coding: [ + Coding(code: "condition-code".asFHIRStringPrimitive()) + ] + ), + id: "condition-id".asFHIRStringPrimitive(), + onset: .dateTime(FHIRPrimitive(try DateTime(date: date))), + patient: Reference(id: "patient-id".asFHIRStringPrimitive()), + verificationStatus: FHIRPrimitive(.confirmed) + ) + } + + static func createProcedure(date: Date, usePeriod: Bool = false) throws -> ModelsDSTU2.Procedure { + let period = ModelsDSTU2.Period() + period.end = try FHIRPrimitive(DateTime(date: date)) + + let performed: ModelsDSTU2.Procedure.PerformedX = usePeriod ? + .period(period) : + .dateTime(FHIRPrimitive(try DateTime(date: date))) + + return ModelsDSTU2.Procedure( + code: CodeableConcept( + coding: [ + Coding(code: "procedure-code".asFHIRStringPrimitive()) + ] + ), + id: "procedure-id".asFHIRStringPrimitive(), + performed: performed, + status: FHIRPrimitive(.completed), + subject: Reference(id: "patient-id".asFHIRStringPrimitive()) + ) + } +} + From dc70d44bd2a557c060e36b9724560dc27a1acbac Mon Sep 17 00:00:00 2001 From: jdisho Date: Tue, 31 Dec 2024 18:42:28 +0100 Subject: [PATCH 2/8] Write tests for `FHIRResource+Search` --- Tests/SpeziFHIRTests/FHIRResourceTests.swift | 48 +++++++++++++++++++- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/Tests/SpeziFHIRTests/FHIRResourceTests.swift b/Tests/SpeziFHIRTests/FHIRResourceTests.swift index 3c2879a..4340820 100644 --- a/Tests/SpeziFHIRTests/FHIRResourceTests.swift +++ b/Tests/SpeziFHIRTests/FHIRResourceTests.swift @@ -8,7 +8,7 @@ import ModelsR4 import ModelsDSTU2 -import SpeziFHIR +@testable import SpeziFHIR import XCTest final class FHIRResourceTests: XCTestCase { @@ -17,7 +17,7 @@ final class FHIRResourceTests: XCTestCase { let calendar = Calendar.current func testModelsR4ResourceInitialization() throws { - let mockObservation = try ModelsR4Mocks.createObservation(date: .now) + let mockObservation = try ModelsR4Mocks.createObservation(date: testDate) let resource = FHIRResource( resource: mockObservation, @@ -151,6 +151,50 @@ final class FHIRResourceTests: XCTestCase { XCTAssertEqual(decodedObservation.code, observation.code) XCTAssertEqual(decodedObservation.status, observation.status) } + + func testMatchesDisplayName() throws { + let observation = try ModelsR4Mocks.createObservation(date: testDate) + + let resource = FHIRResource( + versionedResource: .r4(observation), + displayName: "Test Resource" + ) + + XCTAssertTrue(resource.matchesDisplayName(with: "test")) + XCTAssertTrue(resource.matchesDisplayName(with: "resource")) + XCTAssertTrue(resource.matchesDisplayName(with: " test ")) + XCTAssertTrue(resource.matchesDisplayName(with: "TEST")) + XCTAssertFalse(resource.matchesDisplayName(with: "xyz")) + XCTAssertFalse(resource.matchesDisplayName(with: "")) + } + + func testFilterByDisplayName() throws { + let observation = try ModelsR4Mocks.createObservation(date: testDate) + let patient = try ModelsR4Mocks.createPatient(date: testDate) + let medicationRequest = try ModelsR4Mocks.createMedicationRequest(date: testDate) + + let resource1 = FHIRResource( + versionedResource: .r4(observation), + displayName: "Test Resource1" + ) + + let resource2 = FHIRResource( + versionedResource: .r4(patient), + displayName: "Test Resource2" + ) + + let resource3 = FHIRResource( + versionedResource: .r4(medicationRequest), + displayName: "Test Resource3" + ) + + let resources = [resource1, resource2, resource3] + + XCTAssertEqual(resources.filterByDisplayName(with: "test").count, 3) + XCTAssertEqual(resources.filterByDisplayName(with: "resource1").count, 1) + XCTAssertEqual(resources.filterByDisplayName(with: "xyz").count, 0) + XCTAssertEqual(resources.filterByDisplayName(with: "").count, 3) + } } private extension FHIRResourceTests { From 1c1e9e04b6bb9442f9b28727db5f26ced9dcebd2 Mon Sep 17 00:00:00 2001 From: jdisho Date: Thu, 2 Jan 2025 13:17:06 +0100 Subject: [PATCH 3/8] Write tests for `ResourceProxy+DisplayName` --- Tests/SpeziFHIRTests/FHIRResourceTests.swift | 153 ++++++++++++++++--- 1 file changed, 135 insertions(+), 18 deletions(-) diff --git a/Tests/SpeziFHIRTests/FHIRResourceTests.swift b/Tests/SpeziFHIRTests/FHIRResourceTests.swift index 4340820..aab092b 100644 --- a/Tests/SpeziFHIRTests/FHIRResourceTests.swift +++ b/Tests/SpeziFHIRTests/FHIRResourceTests.swift @@ -6,8 +6,8 @@ // SPDX-License-Identifier: MIT // -import ModelsR4 import ModelsDSTU2 +import ModelsR4 @testable import SpeziFHIR import XCTest @@ -15,15 +15,15 @@ final class FHIRResourceTests: XCTestCase { let testDate = Date(timeIntervalSince1970: 1735123266) let calendar = Calendar.current - + func testModelsR4ResourceInitialization() throws { let mockObservation = try ModelsR4Mocks.createObservation(date: testDate) - + let resource = FHIRResource( resource: mockObservation, displayName: "Test Observation" ) - + XCTAssertEqual(resource.id, "observation-id") XCTAssertEqual(resource.displayName, "Test Observation") XCTAssertEqual(resource.resourceType, "Observation") @@ -41,7 +41,7 @@ final class FHIRResourceTests: XCTestCase { XCTAssertEqual(resource.displayName, "Test Observation") XCTAssertEqual(resource.resourceType, "Observation") } - + func testModelsR4ResourceDates() throws { let modelsR4Resources: [(ModelsR4.Resource, String)] = try [ (ModelsR4Mocks.createCarePlan(date: testDate), "CarePlan"), @@ -62,7 +62,7 @@ final class FHIRResourceTests: XCTestCase { (ModelsR4Mocks.createProvenance(date: testDate), "Provenance"), (ModelsR4Mocks.createSupplyDelivery(date: testDate), "SupplyDelivery") ] - + for (resource, name) in modelsR4Resources { let fhirResource = FHIRResource( versionedResource: .r4(resource), @@ -108,18 +108,18 @@ final class FHIRResourceTests: XCTestCase { id: "test-id".asFHIRStringPrimitive(), status: FHIRPrimitive(.final) ) - + let resource = FHIRResource( versionedResource: .r4(observation), displayName: "Test" ) - + let jsonString = resource.json(withConfiguration: []) - + let jsonData = jsonString.data(using: .utf8)! let decoder = JSONDecoder() let decodedObservation = try decoder.decode(ModelsR4.Observation.self, from: jsonData) - + XCTAssertEqual(decodedObservation.id, observation.id) XCTAssertEqual(decodedObservation.code, observation.code) XCTAssertEqual(decodedObservation.status, observation.status) @@ -135,18 +135,18 @@ final class FHIRResourceTests: XCTestCase { id: "test-id".asFHIRStringPrimitive(), status: FHIRPrimitive(.final) ) - + let resource = FHIRResource( versionedResource: .dstu2(observation), displayName: "Test" ) - + let jsonString = resource.json(withConfiguration: []) - + let jsonData = jsonString.data(using: .utf8)! let decoder = JSONDecoder() let decodedObservation = try decoder.decode(ModelsDSTU2.Observation.self, from: jsonData) - + XCTAssertEqual(decodedObservation.id, observation.id) XCTAssertEqual(decodedObservation.code, observation.code) XCTAssertEqual(decodedObservation.status, observation.status) @@ -154,12 +154,12 @@ final class FHIRResourceTests: XCTestCase { func testMatchesDisplayName() throws { let observation = try ModelsR4Mocks.createObservation(date: testDate) - + let resource = FHIRResource( versionedResource: .r4(observation), displayName: "Test Resource" ) - + XCTAssertTrue(resource.matchesDisplayName(with: "test")) XCTAssertTrue(resource.matchesDisplayName(with: "resource")) XCTAssertTrue(resource.matchesDisplayName(with: " test ")) @@ -172,7 +172,7 @@ final class FHIRResourceTests: XCTestCase { let observation = try ModelsR4Mocks.createObservation(date: testDate) let patient = try ModelsR4Mocks.createPatient(date: testDate) let medicationRequest = try ModelsR4Mocks.createMedicationRequest(date: testDate) - + let resource1 = FHIRResource( versionedResource: .r4(observation), displayName: "Test Resource1" @@ -189,12 +189,129 @@ final class FHIRResourceTests: XCTestCase { ) let resources = [resource1, resource2, resource3] - + XCTAssertEqual(resources.filterByDisplayName(with: "test").count, 3) XCTAssertEqual(resources.filterByDisplayName(with: "resource1").count, 1) XCTAssertEqual(resources.filterByDisplayName(with: "xyz").count, 0) XCTAssertEqual(resources.filterByDisplayName(with: "").count, 3) } + + func testConditionDisplayName() throws { + let mockCondition = try ModelsR4Mocks.createCondition(date: testDate) + + // Test with text value + mockCondition.code = CodeableConcept(text: "Hypertension") + let proxy = ResourceProxy(with: mockCondition) + XCTAssertEqual(proxy.displayName, "Hypertension") + + // Test with no code + mockCondition.code = nil + XCTAssertEqual(proxy.displayName, "Condition") + } + + func testDiagnosticReportDisplayName() throws { + let mockReport = try ModelsR4Mocks.createDiagnosticReport(date: testDate) + + // Test with display coding + mockReport.code.coding = [Coding(display: "Blood Test")] + let proxy = ResourceProxy(with: mockReport) + XCTAssertEqual(proxy.displayName, "Blood Test") + + // Test with no codings + mockReport.code.coding?.removeAll() + XCTAssertEqual(proxy.displayName, "DiagnosticReport") + } + + func testEncounterDisplayName() throws { + let mockEncounter = try ModelsR4Mocks.createEncounter(date: testDate) + + // Test with reason code + mockEncounter.reasonCode = [CodeableConcept(coding: [Coding(display: "Follow-up")])] + let proxy = ResourceProxy(with: mockEncounter) + XCTAssertEqual(proxy.displayName, "Follow-up") + + // Test with encounter type + mockEncounter.reasonCode = nil + mockEncounter.type = [CodeableConcept(coding: [Coding(display: "Office Visit")])] + XCTAssertEqual(proxy.displayName, "Office Visit") + + // Test with no type or reason + mockEncounter.type = nil + XCTAssertEqual(proxy.displayName, "Encounter") + } + + func testImmunizationDisplayName() throws { + let mockImmunization = try ModelsR4Mocks.createImmunization(date: testDate) + + // Test with vaccine text + mockImmunization.vaccineCode.text = "Flu Shot" + let proxy = ResourceProxy(with: mockImmunization) + XCTAssertEqual(proxy.displayName, "Flu Shot") + + // Test with no vaccine text + mockImmunization.vaccineCode.text = nil + XCTAssertEqual(proxy.displayName, "Immunization") + } + + func testMedicationRequestDisplayName() throws { + let mockMedRequest = try ModelsR4Mocks.createMedicationRequest(date: testDate) + + // Test with codeable concept text + if case let .codeableConcept(medicationCode) = mockMedRequest.medication { + medicationCode.text = "Aspirin" + } + let proxy = ResourceProxy(with: mockMedRequest) + XCTAssertEqual(proxy.displayName, "Aspirin") + + // Test with no text in codeable concept + if case let .codeableConcept(medicationCode) = mockMedRequest.medication { + medicationCode.text = nil + } + XCTAssertEqual(proxy.displayName, "MedicationRequest") + + // Test with reference instead of codeable concept + mockMedRequest.medication = .reference(Reference()) + XCTAssertEqual(proxy.displayName, "MedicationRequest") + } + + func testObservationDisplayName() throws { + let mockObservation = try ModelsR4Mocks.createObservation(date: testDate) + + // Test with code text + mockObservation.code.text = "Blood Pressure" + let proxy = ResourceProxy(with: mockObservation) + XCTAssertEqual(proxy.displayName, "Blood Pressure") + + // Test with no code text + mockObservation.code.text = nil + XCTAssertEqual(proxy.displayName, "Observation") + } + + func testProcedureDisplayName() throws { + let mockProcedure = try ModelsR4Mocks.createProcedure(date: testDate) + + // Test with code text + mockProcedure.code = CodeableConcept(text: "Hip Surgery") + let proxy = ResourceProxy(with: mockProcedure) + XCTAssertEqual(proxy.displayName, "Hip Surgery") + + // Test with no code + mockProcedure.code = nil + XCTAssertEqual(proxy.displayName, "Procedure") + } + + func testPatientDisplayName() throws { + let mockPatient = try ModelsR4Mocks.createPatient(date: testDate) + + // Test with name components + mockPatient.name = [HumanName(family: "Doe", given: ["John"])] + let proxy = ResourceProxy(with: mockPatient) + XCTAssertEqual(proxy.displayName, "JohnDoe") + + // Test with no name + mockPatient.name = nil + XCTAssertEqual(proxy.displayName, "Patient") + } } private extension FHIRResourceTests { From 2fa405c3f490ee4c9ecabece20afbfae6182b0e4 Mon Sep 17 00:00:00 2001 From: jdisho Date: Fri, 3 Jan 2025 16:51:12 +0100 Subject: [PATCH 4/8] Fix linting warnings --- Tests/SpeziFHIRTests/FHIRResourceTests.swift | 47 +++++++++----------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/Tests/SpeziFHIRTests/FHIRResourceTests.swift b/Tests/SpeziFHIRTests/FHIRResourceTests.swift index aab092b..d71fc1f 100644 --- a/Tests/SpeziFHIRTests/FHIRResourceTests.swift +++ b/Tests/SpeziFHIRTests/FHIRResourceTests.swift @@ -11,8 +11,8 @@ import ModelsR4 @testable import SpeziFHIR import XCTest +// swiftlint:disable file_length final class FHIRResourceTests: XCTestCase { - let testDate = Date(timeIntervalSince1970: 1735123266) let calendar = Calendar.current @@ -116,7 +116,7 @@ final class FHIRResourceTests: XCTestCase { let jsonString = resource.json(withConfiguration: []) - let jsonData = jsonString.data(using: .utf8)! + let jsonData = jsonString.data(using: .utf8) ?? Data() let decoder = JSONDecoder() let decodedObservation = try decoder.decode(ModelsR4.Observation.self, from: jsonData) @@ -143,7 +143,7 @@ final class FHIRResourceTests: XCTestCase { let jsonString = resource.json(withConfiguration: []) - let jsonData = jsonString.data(using: .utf8)! + let jsonData = jsonString.data(using: .utf8) ?? Data() let decoder = JSONDecoder() let decodedObservation = try decoder.decode(ModelsDSTU2.Observation.self, from: jsonData) @@ -312,10 +312,8 @@ final class FHIRResourceTests: XCTestCase { mockPatient.name = nil XCTAssertEqual(proxy.displayName, "Patient") } -} - -private extension FHIRResourceTests { - func assertEqualDates(_ resourceDate: Date?, _ expectedDate: Date, _ resourceName: String) { + + private func assertEqualDates(_ resourceDate: Date?, _ expectedDate: Date, _ resourceName: String) { guard let resourceDate = resourceDate else { XCTFail("Date should not be nil for \(resourceName)") return @@ -352,7 +350,7 @@ private extension FHIRResourceTests { } } -private struct ModelsR4Mocks { +private enum ModelsR4Mocks { static func createCarePlan(date: Date) throws -> ModelsR4.CarePlan { let period = ModelsR4.Period() period.start = try FHIRPrimitive(DateTime(date: date)) @@ -410,7 +408,7 @@ private struct ModelsR4Mocks { } static func createCondition(date: Date) throws -> ModelsR4.Condition { - return ModelsR4.Condition( + ModelsR4.Condition( id: "condition-id".asFHIRStringPrimitive(), onset: .dateTime(FHIRPrimitive(try DateTime(date: date))), subject: Reference(id: "patient-id".asFHIRStringPrimitive()) @@ -418,14 +416,14 @@ private struct ModelsR4Mocks { } static func createDevice(date: Date) throws -> ModelsR4.Device { - return ModelsR4.Device( + ModelsR4.Device( id: "device-id".asFHIRStringPrimitive(), manufactureDate: FHIRPrimitive(try DateTime(date: date)) ) } static func createDiagnosticReport(date: Date) throws -> ModelsR4.DiagnosticReport { - return ModelsR4.DiagnosticReport( + ModelsR4.DiagnosticReport( code: CodeableConcept(coding: [Coding(code: "code".asFHIRStringPrimitive())]), effective: .dateTime(FHIRPrimitive(try DateTime(date: date))), id: "diagnostic-report-id".asFHIRStringPrimitive(), @@ -492,7 +490,7 @@ private struct ModelsR4Mocks { } static func createImmunization(date: Date) throws -> ModelsR4.Immunization { - return ModelsR4.Immunization( + ModelsR4.Immunization( id: "immunization-id".asFHIRStringPrimitive(), occurrence: .dateTime(FHIRPrimitive(try DateTime(date: date))), patient: Reference(id: "patient-id".asFHIRStringPrimitive()), @@ -506,7 +504,7 @@ private struct ModelsR4Mocks { } static func createMedicationRequest(date: Date) throws -> ModelsR4.MedicationRequest { - return ModelsR4.MedicationRequest( + ModelsR4.MedicationRequest( authoredOn: FHIRPrimitive(try DateTime(date: date)), id: "medication-request-id".asFHIRStringPrimitive(), intent: FHIRPrimitive(.order), @@ -521,7 +519,7 @@ private struct ModelsR4Mocks { } static func createMedicationAdministration(date: Date) throws -> ModelsR4.MedicationAdministration { - return ModelsR4.MedicationAdministration( + ModelsR4.MedicationAdministration( effective: .dateTime(FHIRPrimitive(try DateTime(date: date))), id: "medication-administration-id".asFHIRStringPrimitive(), medication: .codeableConcept( @@ -535,7 +533,7 @@ private struct ModelsR4Mocks { } static func createObservation(date: Date) throws -> ModelsR4.Observation { - return ModelsR4.Observation( + ModelsR4.Observation( code: CodeableConcept(coding: [Coding(code: "code".asFHIRStringPrimitive())]), id: "observation-id".asFHIRStringPrimitive(), issued: FHIRPrimitive(try Instant(date: date)), @@ -544,7 +542,7 @@ private struct ModelsR4Mocks { } static func createProcedure(date: Date) throws -> ModelsR4.Procedure { - return ModelsR4.Procedure( + ModelsR4.Procedure( id: "procedure-id".asFHIRStringPrimitive(), performed: .dateTime(FHIRPrimitive(try DateTime(date: date))), status: FHIRPrimitive(.completed), @@ -553,14 +551,14 @@ private struct ModelsR4Mocks { } static func createPatient(date: Date) throws -> ModelsR4.Patient { - return ModelsR4.Patient( + ModelsR4.Patient( birthDate: FHIRPrimitive(try FHIRDate(date: date)), id: "patient-id".asFHIRStringPrimitive() ) } static func createProvenance(date: Date) throws -> ModelsR4.Provenance { - return ModelsR4.Provenance( + ModelsR4.Provenance( agent: [ ProvenanceAgent( type: CodeableConcept( @@ -578,7 +576,7 @@ private struct ModelsR4Mocks { } static func createSupplyDelivery(date: Date) throws -> ModelsR4.SupplyDelivery { - return ModelsR4.SupplyDelivery( + ModelsR4.SupplyDelivery( id: "supply-delivery-id".asFHIRStringPrimitive(), occurrence: .dateTime(FHIRPrimitive(try DateTime(date: date))), status: FHIRPrimitive(.completed) @@ -587,9 +585,9 @@ private struct ModelsR4Mocks { } -private struct ModelsDSTU2Mocks { +private enum ModelsDSTU2Mocks { static func createObservation(date: Date) throws -> ModelsDSTU2.Observation { - return ModelsDSTU2.Observation( + ModelsDSTU2.Observation( code: CodeableConcept( coding: [ Coding(code: "code".asFHIRStringPrimitive()) @@ -602,7 +600,7 @@ private struct ModelsDSTU2Mocks { } static func createMedicationOrder(date: Date) throws -> ModelsDSTU2.MedicationOrder { - return ModelsDSTU2.MedicationOrder( + ModelsDSTU2.MedicationOrder( dateWritten: FHIRPrimitive(try DateTime(date: date)), id: "medication-order-id".asFHIRStringPrimitive(), medication: .codeableConcept( @@ -615,7 +613,7 @@ private struct ModelsDSTU2Mocks { } static func createMedicationStatement(date: Date) throws -> ModelsDSTU2.MedicationStatement { - return ModelsDSTU2.MedicationStatement( + ModelsDSTU2.MedicationStatement( effective: .dateTime(FHIRPrimitive(try DateTime(date: date))), id: "medication-statement-id".asFHIRStringPrimitive(), medication: .codeableConcept( @@ -629,7 +627,7 @@ private struct ModelsDSTU2Mocks { } static func createCondition(date: Date) throws -> ModelsDSTU2.Condition { - return ModelsDSTU2.Condition( + ModelsDSTU2.Condition( code: CodeableConcept( coding: [ Coding(code: "condition-code".asFHIRStringPrimitive()) @@ -663,4 +661,3 @@ private struct ModelsDSTU2Mocks { ) } } - From 9893669aca32f04e393e17dfbac0b11420f77895 Mon Sep 17 00:00:00 2001 From: Joan Disho Date: Mon, 20 Jan 2025 10:53:44 +0100 Subject: [PATCH 5/8] Simplify impl. of `category` --- .../FHIRResource/FHIRResource+Category.swift | 525 ++---------------- 1 file changed, 42 insertions(+), 483 deletions(-) diff --git a/Sources/SpeziFHIR/FHIRResource/FHIRResource+Category.swift b/Sources/SpeziFHIR/FHIRResource/FHIRResource+Category.swift index 62ea806..8c254aa 100644 --- a/Sources/SpeziFHIR/FHIRResource/FHIRResource+Category.swift +++ b/Sources/SpeziFHIR/FHIRResource/FHIRResource+Category.swift @@ -62,501 +62,60 @@ extension FHIRResource { switch versionedResource { case let .r4(resource): switch ResourceProxy(with: resource) { - case .account: - return FHIRResourceCategory.other - case .activityDefinition: - return FHIRResourceCategory.other - case .adverseEvent: - return FHIRResourceCategory.other case .allergyIntolerance: - return FHIRResourceCategory.allergyIntolerance - case .appointment: - return FHIRResourceCategory.other - case .appointmentResponse: - return FHIRResourceCategory.other - case .auditEvent: - return FHIRResourceCategory.other - case .basic: - return FHIRResourceCategory.other - case .binary: - return FHIRResourceCategory.other - case .biologicallyDerivedProduct: - return FHIRResourceCategory.other - case .bodyStructure: - return FHIRResourceCategory.other - case .bundle: - return FHIRResourceCategory.other - case .capabilityStatement: - return FHIRResourceCategory.other - case .carePlan: - return FHIRResourceCategory.other - case .careTeam: - return FHIRResourceCategory.other - case .catalogEntry: - return FHIRResourceCategory.other - case .chargeItem: - return FHIRResourceCategory.other - case .chargeItemDefinition: - return FHIRResourceCategory.other - case .claim: - return FHIRResourceCategory.other - case .claimResponse: - return FHIRResourceCategory.other - case .clinicalImpression: - return FHIRResourceCategory.other - case .codeSystem: - return FHIRResourceCategory.other - case .communication: - return FHIRResourceCategory.other - case .communicationRequest: - return FHIRResourceCategory.other - case .compartmentDefinition: - return FHIRResourceCategory.other - case .composition: - return FHIRResourceCategory.other - case .conceptMap: - return FHIRResourceCategory.other + return .allergyIntolerance case .condition: - return FHIRResourceCategory.condition - case .consent: - return FHIRResourceCategory.other - case .contract: - return FHIRResourceCategory.other - case .coverage: - return FHIRResourceCategory.other - case .coverageEligibilityRequest: - return FHIRResourceCategory.other - case .coverageEligibilityResponse: - return FHIRResourceCategory.other - case .detectedIssue: - return FHIRResourceCategory.other - case .device: - return FHIRResourceCategory.other - case .deviceDefinition: - return FHIRResourceCategory.other - case .deviceMetric: - return FHIRResourceCategory.other - case .deviceRequest: - return FHIRResourceCategory.other - case .deviceUseStatement: - return FHIRResourceCategory.other + return .condition case .diagnosticReport: - return FHIRResourceCategory.diagnostic - case .documentManifest: - return FHIRResourceCategory.other - case .documentReference: - return FHIRResourceCategory.other - case .domainResource: - return FHIRResourceCategory.other - case .effectEvidenceSynthesis: - return FHIRResourceCategory.other + return .diagnostic case .encounter: - return FHIRResourceCategory.encounter - case .endpoint: - return FHIRResourceCategory.other - case .enrollmentRequest: - return FHIRResourceCategory.other - case .enrollmentResponse: - return FHIRResourceCategory.other - case .episodeOfCare: - return FHIRResourceCategory.other - case .eventDefinition: - return FHIRResourceCategory.other - case .evidence: - return FHIRResourceCategory.other - case .evidenceVariable: - return FHIRResourceCategory.other - case .exampleScenario: - return FHIRResourceCategory.other - case .explanationOfBenefit: - return FHIRResourceCategory.other - case .familyMemberHistory: - return FHIRResourceCategory.other - case .flag: - return FHIRResourceCategory.other - case .goal: - return FHIRResourceCategory.other - case .graphDefinition: - return FHIRResourceCategory.other - case .group: - return FHIRResourceCategory.other - case .guidanceResponse: - return FHIRResourceCategory.other - case .healthcareService: - return FHIRResourceCategory.other - case .imagingStudy: - return FHIRResourceCategory.other - case .immunization: - return FHIRResourceCategory.immunization - case .immunizationEvaluation: - return FHIRResourceCategory.immunization - case .immunizationRecommendation: - return FHIRResourceCategory.immunization - case .implementationGuide: - return FHIRResourceCategory.other - case .insurancePlan: - return FHIRResourceCategory.other - case .invoice: - return FHIRResourceCategory.other - case .library: - return FHIRResourceCategory.other - case .linkage: - return FHIRResourceCategory.other - case .list: - return FHIRResourceCategory.other - case .location: - return FHIRResourceCategory.other - case .measure: - return FHIRResourceCategory.other - case .measureReport: - return FHIRResourceCategory.other - case .media: - return FHIRResourceCategory.other - case .medication: - return FHIRResourceCategory.medication - case .medicationAdministration: - return FHIRResourceCategory.medication - case .medicationDispense: - return FHIRResourceCategory.medication - case .medicationKnowledge: - return FHIRResourceCategory.medication - case .medicationRequest: - return FHIRResourceCategory.medication - case .medicationStatement: - return FHIRResourceCategory.medication - case .medicinalProduct: - return FHIRResourceCategory.other - case .medicinalProductAuthorization: - return FHIRResourceCategory.other - case .medicinalProductContraindication: - return FHIRResourceCategory.other - case .medicinalProductIndication: - return FHIRResourceCategory.other - case .medicinalProductIngredient: - return FHIRResourceCategory.other - case .medicinalProductInteraction: - return FHIRResourceCategory.other - case .medicinalProductManufactured: - return FHIRResourceCategory.other - case .medicinalProductPackaged: - return FHIRResourceCategory.other - case .medicinalProductPharmaceutical: - return FHIRResourceCategory.other - case .medicinalProductUndesirableEffect: - return FHIRResourceCategory.other - case .messageDefinition: - return FHIRResourceCategory.other - case .messageHeader: - return FHIRResourceCategory.other - case .molecularSequence: - return FHIRResourceCategory.other - case .namingSystem: - return FHIRResourceCategory.other - case .nutritionOrder: - return FHIRResourceCategory.other - case .observation: - return FHIRResourceCategory.observation - case .observationDefinition: - return FHIRResourceCategory.observation - case .operationDefinition: - return FHIRResourceCategory.other - case .operationOutcome: - return FHIRResourceCategory.other - case .organization: - return FHIRResourceCategory.other - case .organizationAffiliation: - return FHIRResourceCategory.other - case .parameters: - return FHIRResourceCategory.other - case .patient: - return FHIRResourceCategory.other - case .paymentNotice: - return FHIRResourceCategory.other - case .paymentReconciliation: - return FHIRResourceCategory.other - case .person: - return FHIRResourceCategory.other - case .planDefinition: - return FHIRResourceCategory.other - case .practitioner: - return FHIRResourceCategory.other - case .practitionerRole: - return FHIRResourceCategory.other + return .encounter + case .immunization, + .immunizationEvaluation, + .immunizationRecommendation: + return .immunization + case .medication, + .medicationAdministration, + .medicationDispense, + .medicationKnowledge, + .medicationRequest, + .medicationStatement: + return .medication + case .observation, + .observationDefinition: + return .observation case .procedure: - return FHIRResourceCategory.procedure - case .provenance: - return FHIRResourceCategory.other - case .questionnaire: - return FHIRResourceCategory.other - case .questionnaireResponse: - return FHIRResourceCategory.other - case .relatedPerson: - return FHIRResourceCategory.other - case .requestGroup: - return FHIRResourceCategory.other - case .researchDefinition: - return FHIRResourceCategory.other - case .researchElementDefinition: - return FHIRResourceCategory.other - case .researchStudy: - return FHIRResourceCategory.other - case .researchSubject: - return FHIRResourceCategory.other - case .resource: - return FHIRResourceCategory.other - case .riskAssessment: - return FHIRResourceCategory.other - case .riskEvidenceSynthesis: - return FHIRResourceCategory.other - case .schedule: - return FHIRResourceCategory.other - case .searchParameter: - return FHIRResourceCategory.other - case .serviceRequest: - return FHIRResourceCategory.other - case .slot: - return FHIRResourceCategory.other - case .specimen: - return FHIRResourceCategory.other - case .specimenDefinition: - return FHIRResourceCategory.other - case .structureDefinition: - return FHIRResourceCategory.other - case .structureMap: - return FHIRResourceCategory.other - case .subscription: - return FHIRResourceCategory.other - case .substance: - return FHIRResourceCategory.other - case .substanceNucleicAcid: - return FHIRResourceCategory.other - case .substancePolymer: - return FHIRResourceCategory.other - case .substanceProtein: - return FHIRResourceCategory.other - case .substanceReferenceInformation: - return FHIRResourceCategory.other - case .substanceSourceMaterial: - return FHIRResourceCategory.other - case .substanceSpecification: - return FHIRResourceCategory.other - case .supplyDelivery: - return FHIRResourceCategory.other - case .supplyRequest: - return FHIRResourceCategory.other - case .task: - return FHIRResourceCategory.other - case .terminologyCapabilities: - return FHIRResourceCategory.other - case .testReport: - return FHIRResourceCategory.other - case .testScript: - return FHIRResourceCategory.other - case .valueSet: - return FHIRResourceCategory.other - case .verificationResult: - return FHIRResourceCategory.other - case .visionPrescription: - return FHIRResourceCategory.other - case .unrecognized: - return FHIRResourceCategory.other + return .procedure + default: + return .other } case let .dstu2(resource): switch ResourceProxy(with: resource) { - case .account: - return FHIRResourceCategory.other case .allergyIntolerance: - return FHIRResourceCategory.allergyIntolerance - case .appointment: - return FHIRResourceCategory.other - case .appointmentResponse: - return FHIRResourceCategory.other - case .auditEvent: - return FHIRResourceCategory.other - case .basic: - return FHIRResourceCategory.other - case .binary: - return FHIRResourceCategory.other - case .bodySite: - return FHIRResourceCategory.other - case .bundle: - return FHIRResourceCategory.other - case .carePlan: - return FHIRResourceCategory.other - case .claim: - return FHIRResourceCategory.other - case .claimResponse: - return FHIRResourceCategory.other - case .clinicalImpression: - return FHIRResourceCategory.other - case .communication: - return FHIRResourceCategory.other - case .communicationRequest: - return FHIRResourceCategory.other - case .composition: - return FHIRResourceCategory.other - case .conceptMap: - return FHIRResourceCategory.other + return .allergyIntolerance case .condition: - return FHIRResourceCategory.condition - case .conformance: - return FHIRResourceCategory.other - case .contract: - return FHIRResourceCategory.other - case .coverage: - return FHIRResourceCategory.other - case .dataElement: - return FHIRResourceCategory.other - case .detectedIssue: - return FHIRResourceCategory.other - case .device: - return FHIRResourceCategory.other - case .deviceComponent: - return FHIRResourceCategory.other - case .deviceMetric: - return FHIRResourceCategory.other - case .deviceUseRequest: - return FHIRResourceCategory.other - case .deviceUseStatement: - return FHIRResourceCategory.other - case .diagnosticOrder: - return FHIRResourceCategory.diagnostic - case .diagnosticReport: - return FHIRResourceCategory.diagnostic - case .documentManifest: - return FHIRResourceCategory.other - case .documentReference: - return FHIRResourceCategory.other - case .domainResource: - return FHIRResourceCategory.other - case .eligibilityRequest: - return FHIRResourceCategory.other - case .eligibilityResponse: - return FHIRResourceCategory.other + return .condition + case .diagnosticOrder, + .diagnosticReport: + return .diagnostic case .encounter: - return FHIRResourceCategory.encounter - case .enrollmentRequest: - return FHIRResourceCategory.other - case .enrollmentResponse: - return FHIRResourceCategory.other - case .episodeOfCare: - return FHIRResourceCategory.other - case .explanationOfBenefit: - return FHIRResourceCategory.other - case .familyMemberHistory: - return FHIRResourceCategory.other - case .flag: - return FHIRResourceCategory.other - case .goal: - return FHIRResourceCategory.other - case .group: - return FHIRResourceCategory.other - case .healthcareService: - return FHIRResourceCategory.other - case .imagingObjectSelection: - return FHIRResourceCategory.other - case .imagingStudy: - return FHIRResourceCategory.other - case .immunization: - return FHIRResourceCategory.immunization - case .immunizationRecommendation: - return FHIRResourceCategory.immunization - case .implementationGuide: - return FHIRResourceCategory.other - case .list: - return FHIRResourceCategory.other - case .location: - return FHIRResourceCategory.other - case .media: - return FHIRResourceCategory.other - case .medication: - return FHIRResourceCategory.medication - case .medicationAdministration: - return FHIRResourceCategory.medication - case .medicationDispense: - return FHIRResourceCategory.medication - case .medicationOrder: - return FHIRResourceCategory.medication - case .medicationStatement: - return FHIRResourceCategory.medication - case .messageHeader: - return FHIRResourceCategory.other - case .namingSystem: - return FHIRResourceCategory.other - case .nutritionOrder: - return FHIRResourceCategory.other + return .encounter + case .immunization, + .immunizationRecommendation: + return .immunization + case .medication, + .medicationAdministration, + .medicationDispense, + .medicationOrder, + .medicationStatement: + return .medication case .observation: - return FHIRResourceCategory.observation - case .operationDefinition: - return FHIRResourceCategory.other - case .operationOutcome: - return FHIRResourceCategory.other - case .order: - return FHIRResourceCategory.other - case .orderResponse: - return FHIRResourceCategory.other - case .organization: - return FHIRResourceCategory.other - case .parameters: - return FHIRResourceCategory.other - case .patient: - return FHIRResourceCategory.other - case .paymentNotice: - return FHIRResourceCategory.other - case .paymentReconciliation: - return FHIRResourceCategory.other - case .person: - return FHIRResourceCategory.other - case .practitioner: - return FHIRResourceCategory.other - case .procedure: - return FHIRResourceCategory.procedure - case .procedureRequest: - return FHIRResourceCategory.procedure - case .processRequest: - return FHIRResourceCategory.other - case .processResponse: - return FHIRResourceCategory.other - case .provenance: - return FHIRResourceCategory.other - case .questionnaire: - return FHIRResourceCategory.other - case .questionnaireResponse: - return FHIRResourceCategory.other - case .referralRequest: - return FHIRResourceCategory.other - case .relatedPerson: - return FHIRResourceCategory.other - case .resource: - return FHIRResourceCategory.other - case .riskAssessment: - return FHIRResourceCategory.other - case .schedule: - return FHIRResourceCategory.other - case .searchParameter: - return FHIRResourceCategory.other - case .slot: - return FHIRResourceCategory.other - case .specimen: - return FHIRResourceCategory.other - case .structureDefinition: - return FHIRResourceCategory.other - case .subscription: - return FHIRResourceCategory.other - case .substance: - return FHIRResourceCategory.other - case .supplyDelivery: - return FHIRResourceCategory.other - case .supplyRequest: - return FHIRResourceCategory.other - case .testScript: - return FHIRResourceCategory.other - case .valueSet: - return FHIRResourceCategory.other - case .visionPrescription: - return FHIRResourceCategory.other - case .unrecognized: - return FHIRResourceCategory.other + return .observation + case .procedure, + .procedureRequest: + return .procedure + default: + return .other } } } From 114d641fe0fabf9a94017b01e998ecd37b60e245 Mon Sep 17 00:00:00 2001 From: Joan Disho Date: Mon, 20 Jan 2025 14:54:35 +0100 Subject: [PATCH 6/8] Write tests for `FHIRResource+Category` --- Tests/SpeziFHIRTests/FHIRResourceTests.swift | 265 ++++++++++++++++++- 1 file changed, 264 insertions(+), 1 deletion(-) diff --git a/Tests/SpeziFHIRTests/FHIRResourceTests.swift b/Tests/SpeziFHIRTests/FHIRResourceTests.swift index d71fc1f..56f29ec 100644 --- a/Tests/SpeziFHIRTests/FHIRResourceTests.swift +++ b/Tests/SpeziFHIRTests/FHIRResourceTests.swift @@ -299,7 +299,7 @@ final class FHIRResourceTests: XCTestCase { mockProcedure.code = nil XCTAssertEqual(proxy.displayName, "Procedure") } - + func testPatientDisplayName() throws { let mockPatient = try ModelsR4Mocks.createPatient(date: testDate) @@ -312,6 +312,89 @@ final class FHIRResourceTests: XCTestCase { mockPatient.name = nil XCTAssertEqual(proxy.displayName, "Patient") } + + func testModelsR4Categories() throws { + // Define test cases as tuples of (resource creator function, expected category) + let testCases: [(resource: () throws -> ModelsR4.Resource, category: FHIRResource.FHIRResourceCategory)] = [ + // Observation cases + ({ try ModelsR4Mocks.createObservation(date: self.testDate) }, .observation), + ({ ModelsR4Mocks.createObservationDefinition() }, .observation), + + // Single resource cases + ({ try ModelsR4Mocks.createEncounter(date: self.testDate) }, .encounter), + ({ try ModelsR4Mocks.createCondition(date: self.testDate) }, .condition), + ({ try ModelsR4Mocks.createDiagnosticReport(date: self.testDate) }, .diagnostic), + ({ try ModelsR4Mocks.createProcedure(date: self.testDate) }, .procedure), + ({ ModelsR4Mocks.createAllergyIntolerance() }, .allergyIntolerance), + + // Immunization cases + ({ try ModelsR4Mocks.createImmunization(date: self.testDate) }, .immunization), + ({ ModelsR4Mocks.createImmunizationEvaluation() }, .immunization), + ({ try ModelsR4Mocks.createImmunizationRecommendation(date: self.testDate) }, .immunization), + + // Medication cases + ({ ModelsR4Mocks.createMedication() }, .medication), + ({ try ModelsR4Mocks.createMedicationRequest(date: self.testDate) }, .medication), + ({ try ModelsR4Mocks.createMedicationAdministration(date: self.testDate) }, .medication), + ({ ModelsR4Mocks.createMedicationDispense() }, .medication), + ({ ModelsR4Mocks.createMedicationKnowledge() }, .medication), + ({ ModelsR4Mocks.createMedicationStatement() }, .medication) + ] + + try testCases.forEach { creator, expectedCategory in + let resource = try creator() + let fhirResource = FHIRResource(versionedResource: .r4(resource), displayName: "") + XCTAssertEqual(fhirResource.category, expectedCategory, "Failed for resource type: \(type(of: resource))") + } + } + + func testModelsDSTU2Categories() throws { + let testCases: [(resource: () throws -> ModelsDSTU2.Resource, category: FHIRResource.FHIRResourceCategory)] = [ + // Single resource cases + ({ ModelsDSTU2Mocks.createAllergyIntolerance() }, .allergyIntolerance), + ({ ModelsDSTU2Mocks.createEncounter() }, .encounter), + ({ try ModelsDSTU2Mocks.createObservation(date: self.testDate) }, .observation), + ({ try ModelsDSTU2Mocks.createCondition(date: self.testDate) }, .condition), + + // Diagnostic cases + ({ try ModelsDSTU2Mocks.createDiagnosticReport(date: self.testDate) }, .diagnostic), + ({ try ModelsDSTU2Mocks.createDiagnosticOrder(date: self.testDate) }, .diagnostic), + + // Procedure cases + ({ try ModelsDSTU2Mocks.createProcedure(date: self.testDate) }, .procedure), + ({ ModelsDSTU2Mocks.createProcedureRequest() }, .procedure), + + // Immunization cases + ({ try ModelsDSTU2Mocks.createImmunization(date: self.testDate) }, .immunization), + ({ ModelsDSTU2Mocks.createImmunizationRecommendation() }, .immunization), + + // Medication cases + ({ ModelsDSTU2Mocks.createMedication() }, .medication), + ({ try ModelsDSTU2Mocks.createMedicationOrder(date: self.testDate) }, .medication), + ({ try ModelsDSTU2Mocks.createMedicationAdministration(date: self.testDate) }, .medication), + ({ ModelsDSTU2Mocks.createMedicationDispense() }, .medication), + ({ try ModelsDSTU2Mocks.createMedicationStatement(date: self.testDate) }, .medication) + ] + + try testCases.forEach { creator, expectedCategory in + let resource = try creator() + let fhirResource = FHIRResource(versionedResource: .dstu2(resource), displayName: "") + XCTAssertEqual(fhirResource.category, expectedCategory, "Failed for resource type: \(type(of: resource))") + } + } + + @MainActor + func testCategoryKeyPaths() { + XCTAssertEqual(FHIRResource.FHIRResourceCategory.observation.storeKeyPath, \FHIRStore.observations) + XCTAssertEqual(FHIRResource.FHIRResourceCategory.encounter.storeKeyPath, \FHIRStore.encounters) + XCTAssertEqual(FHIRResource.FHIRResourceCategory.condition.storeKeyPath, \FHIRStore.conditions) + XCTAssertEqual(FHIRResource.FHIRResourceCategory.diagnostic.storeKeyPath, \FHIRStore.diagnostics) + XCTAssertEqual(FHIRResource.FHIRResourceCategory.procedure.storeKeyPath, \FHIRStore.procedures) + XCTAssertEqual(FHIRResource.FHIRResourceCategory.immunization.storeKeyPath, \FHIRStore.immunizations) + XCTAssertEqual(FHIRResource.FHIRResourceCategory.allergyIntolerance.storeKeyPath, \FHIRStore.allergyIntolerances) + XCTAssertEqual(FHIRResource.FHIRResourceCategory.medication.storeKeyPath, \FHIRStore.medications) + XCTAssertEqual(FHIRResource.FHIRResourceCategory.other.storeKeyPath, \FHIRStore.otherResources) + } private func assertEqualDates(_ resourceDate: Date?, _ expectedDate: Date, _ resourceName: String) { guard let resourceDate = resourceDate else { @@ -351,6 +434,13 @@ final class FHIRResourceTests: XCTestCase { } private enum ModelsR4Mocks { + static func createAllergyIntolerance() -> ModelsR4.AllergyIntolerance { + ModelsR4.AllergyIntolerance( + id: "allergy-intolerance-id".asFHIRStringPrimitive(), + patient: Reference(id: "patient-id".asFHIRStringPrimitive()) + ) + } + static func createCarePlan(date: Date) throws -> ModelsR4.CarePlan { let period = ModelsR4.Period() period.start = try FHIRPrimitive(DateTime(date: date)) @@ -502,6 +592,54 @@ private enum ModelsR4Mocks { ) ) } + + static func createImmunizationEvaluation() -> ModelsR4.ImmunizationEvaluation { + ModelsR4.ImmunizationEvaluation( + doseStatus: CodeableConcept( + coding: [ + Coding(code: "dose-status".asFHIRStringPrimitive()) + ] + ), + id: "immunization-eval-id".asFHIRStringPrimitive(), + immunizationEvent: Reference(id: "event-id".asFHIRStringPrimitive()), + patient: Reference(id: "patient-id".asFHIRStringPrimitive()), + status: FHIRPrimitive(.completed), + targetDisease: CodeableConcept( + coding: [ + Coding(code: "targe-disease".asFHIRStringPrimitive()) + ] + ) + ) + } + + static func createImmunizationRecommendation(date: Date) throws -> ModelsR4.ImmunizationRecommendation { + ModelsR4.ImmunizationRecommendation( + date: FHIRPrimitive(try DateTime(date: date)), + id: "immunization-recommendation-id".asFHIRStringPrimitive(), + patient: Reference(id: "patient-id".asFHIRStringPrimitive()), + recommendation: [] + ) + } + + static func createMedication() -> ModelsR4.Medication { + ModelsR4.Medication(id: "medication-id".asFHIRStringPrimitive()) + } + + static func createMedicationDispense() -> ModelsR4.MedicationDispense { + ModelsR4.MedicationDispense( + id: "medication-dispense-id".asFHIRStringPrimitive(), + medication: .codeableConcept( + CodeableConcept(coding: [ + Coding(code: "med-code".asFHIRStringPrimitive()) + ]) + ), + status: FHIRPrimitive(.completed) + ) + } + + static func createMedicationKnowledge() -> ModelsR4.MedicationKnowledge { + ModelsR4.MedicationKnowledge(id: "medication-knowledge-id".asFHIRStringPrimitive()) + } static func createMedicationRequest(date: Date) throws -> ModelsR4.MedicationRequest { ModelsR4.MedicationRequest( @@ -532,6 +670,19 @@ private enum ModelsR4Mocks { ) } + static func createMedicationStatement() -> ModelsR4.MedicationStatement { + ModelsR4.MedicationStatement( + id: "medication-statement-id".asFHIRStringPrimitive(), + medication: .codeableConcept( + CodeableConcept(coding: [ + Coding(code: "med-code".asFHIRStringPrimitive()) + ]) + ), + status: FHIRPrimitive(.completed), + subject: Reference(id: "patient-id".asFHIRStringPrimitive()) + ) + } + static func createObservation(date: Date) throws -> ModelsR4.Observation { ModelsR4.Observation( code: CodeableConcept(coding: [Coding(code: "code".asFHIRStringPrimitive())]), @@ -541,6 +692,13 @@ private enum ModelsR4Mocks { ) } + static func createObservationDefinition() -> ModelsR4.ObservationDefinition { + ModelsR4.ObservationDefinition( + code: CodeableConcept(coding: [Coding(code: "code".asFHIRStringPrimitive())]), + id: "observation-definition-id".asFHIRStringPrimitive() + ) + } + static func createProcedure(date: Date) throws -> ModelsR4.Procedure { ModelsR4.Procedure( id: "procedure-id".asFHIRStringPrimitive(), @@ -586,6 +744,40 @@ private enum ModelsR4Mocks { private enum ModelsDSTU2Mocks { + static func createAllergyIntolerance() -> ModelsDSTU2.AllergyIntolerance { + ModelsDSTU2.AllergyIntolerance( + id: "allergy-intolerance-id".asFHIRStringPrimitive(), + patient: Reference(id: "patient-id".asFHIRStringPrimitive()), + substance: CodeableConcept( + coding: [ + Coding(code: "code".asFHIRStringPrimitive()) + ] + ) + ) + } + + static func createDiagnosticOrder(date: Date) throws -> ModelsDSTU2.DiagnosticOrder { + ModelsDSTU2.DiagnosticOrder( + id: "diagnostic-order-id".asFHIRStringPrimitive(), + subject: Reference(id: "patient-id".asFHIRStringPrimitive()) + ) + } + + static func createDiagnosticReport(date: Date) throws -> ModelsDSTU2.DiagnosticReport { + ModelsDSTU2.DiagnosticReport( + code: CodeableConcept( + coding: [ + Coding(code: "code".asFHIRStringPrimitive()) + ] + ), + effective: .dateTime(FHIRPrimitive(try DateTime(date: date))), + id: "diagnostic-report-id".asFHIRStringPrimitive(), + issued: FHIRPrimitive(try Instant(date: date)), + performer: Reference(id: "patient-id".asFHIRStringPrimitive()), + status: FHIRPrimitive(.final), + subject: Reference(id: "patient-id".asFHIRStringPrimitive())) + } + static func createObservation(date: Date) throws -> ModelsDSTU2.Observation { ModelsDSTU2.Observation( code: CodeableConcept( @@ -599,6 +791,59 @@ private enum ModelsDSTU2Mocks { ) } + static func createImmunization(date: Date) throws -> ModelsDSTU2.Immunization { + ModelsDSTU2.Immunization( + id: "immunization-id".asFHIRStringPrimitive(), + patient: Reference(id: "patient-id".asFHIRStringPrimitive()), + reported: FHIRPrimitive(true), + status: FHIRPrimitive(.completed), + vaccineCode: CodeableConcept( + coding: [ + Coding(code: "code".asFHIRStringPrimitive()) + ] + ), + wasNotGiven: FHIRPrimitive(true) + ) + } + + static func createImmunizationRecommendation() -> ModelsDSTU2.ImmunizationRecommendation { + ModelsDSTU2.ImmunizationRecommendation( + id: "immunization-recommendation-id".asFHIRStringPrimitive(), + patient: Reference(id: "patient-id".asFHIRStringPrimitive()), + recommendation: [] + ) + } + + static func createMedication() -> ModelsDSTU2.Medication { + ModelsDSTU2.Medication(id: "medication-id".asFHIRStringPrimitive()) + } + + static func createMedicationAdministration(date: Date) throws -> ModelsDSTU2.MedicationAdministration { + ModelsDSTU2.MedicationAdministration( + effectiveTime: .dateTime(FHIRPrimitive(try DateTime(date: date))), + id: "medication-administration-id".asFHIRStringPrimitive(), + medication: .codeableConcept( + CodeableConcept(coding: [ + Coding(code: "med-code".asFHIRStringPrimitive()) + ]) + ), + patient: Reference(id: "patient-id".asFHIRStringPrimitive()), + status: FHIRPrimitive(.completed) + ) + } + + static func createMedicationDispense() -> ModelsDSTU2.MedicationDispense { + ModelsDSTU2.MedicationDispense( + id: "medication-dispense-id".asFHIRStringPrimitive(), + medication: .codeableConcept( + CodeableConcept(coding: [ + Coding(code: "med-code".asFHIRStringPrimitive()) + ]) + ), + status: FHIRPrimitive(.completed) + ) + } + static func createMedicationOrder(date: Date) throws -> ModelsDSTU2.MedicationOrder { ModelsDSTU2.MedicationOrder( dateWritten: FHIRPrimitive(try DateTime(date: date)), @@ -640,6 +885,10 @@ private enum ModelsDSTU2Mocks { ) } + static func createEncounter() -> ModelsDSTU2.Encounter { + ModelsDSTU2.Encounter(id: "encounter-id".asFHIRStringPrimitive(), status: FHIRPrimitive(.finished)) + } + static func createProcedure(date: Date, usePeriod: Bool = false) throws -> ModelsDSTU2.Procedure { let period = ModelsDSTU2.Period() period.end = try FHIRPrimitive(DateTime(date: date)) @@ -660,4 +909,18 @@ private enum ModelsDSTU2Mocks { subject: Reference(id: "patient-id".asFHIRStringPrimitive()) ) } + + static func createProcedureRequest() -> ModelsDSTU2.ProcedureRequest { + ModelsDSTU2.ProcedureRequest( + code: CodeableConcept( + coding: [ + Coding(code: "procedure-code".asFHIRStringPrimitive()) + ] + ), + id: "procedure-id".asFHIRStringPrimitive(), + performer: Reference(id: "performer-id".asFHIRStringPrimitive()), + status: FHIRPrimitive(.completed), + subject: Reference(id: "patient-id".asFHIRStringPrimitive()) + ) + } } From 869dde29e87ac1a9c87e7527096129a98c893366 Mon Sep 17 00:00:00 2001 From: Joan Disho Date: Tue, 21 Jan 2025 09:55:26 +0100 Subject: [PATCH 7/8] Revert "Simplify impl. of `category`" This reverts commit 9893669aca32f04e393e17dfbac0b11420f77895. --- .../FHIRResource/FHIRResource+Category.swift | 525 ++++++++++++++++-- 1 file changed, 483 insertions(+), 42 deletions(-) diff --git a/Sources/SpeziFHIR/FHIRResource/FHIRResource+Category.swift b/Sources/SpeziFHIR/FHIRResource/FHIRResource+Category.swift index 8c254aa..62ea806 100644 --- a/Sources/SpeziFHIR/FHIRResource/FHIRResource+Category.swift +++ b/Sources/SpeziFHIR/FHIRResource/FHIRResource+Category.swift @@ -62,60 +62,501 @@ extension FHIRResource { switch versionedResource { case let .r4(resource): switch ResourceProxy(with: resource) { + case .account: + return FHIRResourceCategory.other + case .activityDefinition: + return FHIRResourceCategory.other + case .adverseEvent: + return FHIRResourceCategory.other case .allergyIntolerance: - return .allergyIntolerance + return FHIRResourceCategory.allergyIntolerance + case .appointment: + return FHIRResourceCategory.other + case .appointmentResponse: + return FHIRResourceCategory.other + case .auditEvent: + return FHIRResourceCategory.other + case .basic: + return FHIRResourceCategory.other + case .binary: + return FHIRResourceCategory.other + case .biologicallyDerivedProduct: + return FHIRResourceCategory.other + case .bodyStructure: + return FHIRResourceCategory.other + case .bundle: + return FHIRResourceCategory.other + case .capabilityStatement: + return FHIRResourceCategory.other + case .carePlan: + return FHIRResourceCategory.other + case .careTeam: + return FHIRResourceCategory.other + case .catalogEntry: + return FHIRResourceCategory.other + case .chargeItem: + return FHIRResourceCategory.other + case .chargeItemDefinition: + return FHIRResourceCategory.other + case .claim: + return FHIRResourceCategory.other + case .claimResponse: + return FHIRResourceCategory.other + case .clinicalImpression: + return FHIRResourceCategory.other + case .codeSystem: + return FHIRResourceCategory.other + case .communication: + return FHIRResourceCategory.other + case .communicationRequest: + return FHIRResourceCategory.other + case .compartmentDefinition: + return FHIRResourceCategory.other + case .composition: + return FHIRResourceCategory.other + case .conceptMap: + return FHIRResourceCategory.other case .condition: - return .condition + return FHIRResourceCategory.condition + case .consent: + return FHIRResourceCategory.other + case .contract: + return FHIRResourceCategory.other + case .coverage: + return FHIRResourceCategory.other + case .coverageEligibilityRequest: + return FHIRResourceCategory.other + case .coverageEligibilityResponse: + return FHIRResourceCategory.other + case .detectedIssue: + return FHIRResourceCategory.other + case .device: + return FHIRResourceCategory.other + case .deviceDefinition: + return FHIRResourceCategory.other + case .deviceMetric: + return FHIRResourceCategory.other + case .deviceRequest: + return FHIRResourceCategory.other + case .deviceUseStatement: + return FHIRResourceCategory.other case .diagnosticReport: - return .diagnostic + return FHIRResourceCategory.diagnostic + case .documentManifest: + return FHIRResourceCategory.other + case .documentReference: + return FHIRResourceCategory.other + case .domainResource: + return FHIRResourceCategory.other + case .effectEvidenceSynthesis: + return FHIRResourceCategory.other case .encounter: - return .encounter - case .immunization, - .immunizationEvaluation, - .immunizationRecommendation: - return .immunization - case .medication, - .medicationAdministration, - .medicationDispense, - .medicationKnowledge, - .medicationRequest, - .medicationStatement: - return .medication - case .observation, - .observationDefinition: - return .observation + return FHIRResourceCategory.encounter + case .endpoint: + return FHIRResourceCategory.other + case .enrollmentRequest: + return FHIRResourceCategory.other + case .enrollmentResponse: + return FHIRResourceCategory.other + case .episodeOfCare: + return FHIRResourceCategory.other + case .eventDefinition: + return FHIRResourceCategory.other + case .evidence: + return FHIRResourceCategory.other + case .evidenceVariable: + return FHIRResourceCategory.other + case .exampleScenario: + return FHIRResourceCategory.other + case .explanationOfBenefit: + return FHIRResourceCategory.other + case .familyMemberHistory: + return FHIRResourceCategory.other + case .flag: + return FHIRResourceCategory.other + case .goal: + return FHIRResourceCategory.other + case .graphDefinition: + return FHIRResourceCategory.other + case .group: + return FHIRResourceCategory.other + case .guidanceResponse: + return FHIRResourceCategory.other + case .healthcareService: + return FHIRResourceCategory.other + case .imagingStudy: + return FHIRResourceCategory.other + case .immunization: + return FHIRResourceCategory.immunization + case .immunizationEvaluation: + return FHIRResourceCategory.immunization + case .immunizationRecommendation: + return FHIRResourceCategory.immunization + case .implementationGuide: + return FHIRResourceCategory.other + case .insurancePlan: + return FHIRResourceCategory.other + case .invoice: + return FHIRResourceCategory.other + case .library: + return FHIRResourceCategory.other + case .linkage: + return FHIRResourceCategory.other + case .list: + return FHIRResourceCategory.other + case .location: + return FHIRResourceCategory.other + case .measure: + return FHIRResourceCategory.other + case .measureReport: + return FHIRResourceCategory.other + case .media: + return FHIRResourceCategory.other + case .medication: + return FHIRResourceCategory.medication + case .medicationAdministration: + return FHIRResourceCategory.medication + case .medicationDispense: + return FHIRResourceCategory.medication + case .medicationKnowledge: + return FHIRResourceCategory.medication + case .medicationRequest: + return FHIRResourceCategory.medication + case .medicationStatement: + return FHIRResourceCategory.medication + case .medicinalProduct: + return FHIRResourceCategory.other + case .medicinalProductAuthorization: + return FHIRResourceCategory.other + case .medicinalProductContraindication: + return FHIRResourceCategory.other + case .medicinalProductIndication: + return FHIRResourceCategory.other + case .medicinalProductIngredient: + return FHIRResourceCategory.other + case .medicinalProductInteraction: + return FHIRResourceCategory.other + case .medicinalProductManufactured: + return FHIRResourceCategory.other + case .medicinalProductPackaged: + return FHIRResourceCategory.other + case .medicinalProductPharmaceutical: + return FHIRResourceCategory.other + case .medicinalProductUndesirableEffect: + return FHIRResourceCategory.other + case .messageDefinition: + return FHIRResourceCategory.other + case .messageHeader: + return FHIRResourceCategory.other + case .molecularSequence: + return FHIRResourceCategory.other + case .namingSystem: + return FHIRResourceCategory.other + case .nutritionOrder: + return FHIRResourceCategory.other + case .observation: + return FHIRResourceCategory.observation + case .observationDefinition: + return FHIRResourceCategory.observation + case .operationDefinition: + return FHIRResourceCategory.other + case .operationOutcome: + return FHIRResourceCategory.other + case .organization: + return FHIRResourceCategory.other + case .organizationAffiliation: + return FHIRResourceCategory.other + case .parameters: + return FHIRResourceCategory.other + case .patient: + return FHIRResourceCategory.other + case .paymentNotice: + return FHIRResourceCategory.other + case .paymentReconciliation: + return FHIRResourceCategory.other + case .person: + return FHIRResourceCategory.other + case .planDefinition: + return FHIRResourceCategory.other + case .practitioner: + return FHIRResourceCategory.other + case .practitionerRole: + return FHIRResourceCategory.other case .procedure: - return .procedure - default: - return .other + return FHIRResourceCategory.procedure + case .provenance: + return FHIRResourceCategory.other + case .questionnaire: + return FHIRResourceCategory.other + case .questionnaireResponse: + return FHIRResourceCategory.other + case .relatedPerson: + return FHIRResourceCategory.other + case .requestGroup: + return FHIRResourceCategory.other + case .researchDefinition: + return FHIRResourceCategory.other + case .researchElementDefinition: + return FHIRResourceCategory.other + case .researchStudy: + return FHIRResourceCategory.other + case .researchSubject: + return FHIRResourceCategory.other + case .resource: + return FHIRResourceCategory.other + case .riskAssessment: + return FHIRResourceCategory.other + case .riskEvidenceSynthesis: + return FHIRResourceCategory.other + case .schedule: + return FHIRResourceCategory.other + case .searchParameter: + return FHIRResourceCategory.other + case .serviceRequest: + return FHIRResourceCategory.other + case .slot: + return FHIRResourceCategory.other + case .specimen: + return FHIRResourceCategory.other + case .specimenDefinition: + return FHIRResourceCategory.other + case .structureDefinition: + return FHIRResourceCategory.other + case .structureMap: + return FHIRResourceCategory.other + case .subscription: + return FHIRResourceCategory.other + case .substance: + return FHIRResourceCategory.other + case .substanceNucleicAcid: + return FHIRResourceCategory.other + case .substancePolymer: + return FHIRResourceCategory.other + case .substanceProtein: + return FHIRResourceCategory.other + case .substanceReferenceInformation: + return FHIRResourceCategory.other + case .substanceSourceMaterial: + return FHIRResourceCategory.other + case .substanceSpecification: + return FHIRResourceCategory.other + case .supplyDelivery: + return FHIRResourceCategory.other + case .supplyRequest: + return FHIRResourceCategory.other + case .task: + return FHIRResourceCategory.other + case .terminologyCapabilities: + return FHIRResourceCategory.other + case .testReport: + return FHIRResourceCategory.other + case .testScript: + return FHIRResourceCategory.other + case .valueSet: + return FHIRResourceCategory.other + case .verificationResult: + return FHIRResourceCategory.other + case .visionPrescription: + return FHIRResourceCategory.other + case .unrecognized: + return FHIRResourceCategory.other } case let .dstu2(resource): switch ResourceProxy(with: resource) { + case .account: + return FHIRResourceCategory.other case .allergyIntolerance: - return .allergyIntolerance + return FHIRResourceCategory.allergyIntolerance + case .appointment: + return FHIRResourceCategory.other + case .appointmentResponse: + return FHIRResourceCategory.other + case .auditEvent: + return FHIRResourceCategory.other + case .basic: + return FHIRResourceCategory.other + case .binary: + return FHIRResourceCategory.other + case .bodySite: + return FHIRResourceCategory.other + case .bundle: + return FHIRResourceCategory.other + case .carePlan: + return FHIRResourceCategory.other + case .claim: + return FHIRResourceCategory.other + case .claimResponse: + return FHIRResourceCategory.other + case .clinicalImpression: + return FHIRResourceCategory.other + case .communication: + return FHIRResourceCategory.other + case .communicationRequest: + return FHIRResourceCategory.other + case .composition: + return FHIRResourceCategory.other + case .conceptMap: + return FHIRResourceCategory.other case .condition: - return .condition - case .diagnosticOrder, - .diagnosticReport: - return .diagnostic + return FHIRResourceCategory.condition + case .conformance: + return FHIRResourceCategory.other + case .contract: + return FHIRResourceCategory.other + case .coverage: + return FHIRResourceCategory.other + case .dataElement: + return FHIRResourceCategory.other + case .detectedIssue: + return FHIRResourceCategory.other + case .device: + return FHIRResourceCategory.other + case .deviceComponent: + return FHIRResourceCategory.other + case .deviceMetric: + return FHIRResourceCategory.other + case .deviceUseRequest: + return FHIRResourceCategory.other + case .deviceUseStatement: + return FHIRResourceCategory.other + case .diagnosticOrder: + return FHIRResourceCategory.diagnostic + case .diagnosticReport: + return FHIRResourceCategory.diagnostic + case .documentManifest: + return FHIRResourceCategory.other + case .documentReference: + return FHIRResourceCategory.other + case .domainResource: + return FHIRResourceCategory.other + case .eligibilityRequest: + return FHIRResourceCategory.other + case .eligibilityResponse: + return FHIRResourceCategory.other case .encounter: - return .encounter - case .immunization, - .immunizationRecommendation: - return .immunization - case .medication, - .medicationAdministration, - .medicationDispense, - .medicationOrder, - .medicationStatement: - return .medication + return FHIRResourceCategory.encounter + case .enrollmentRequest: + return FHIRResourceCategory.other + case .enrollmentResponse: + return FHIRResourceCategory.other + case .episodeOfCare: + return FHIRResourceCategory.other + case .explanationOfBenefit: + return FHIRResourceCategory.other + case .familyMemberHistory: + return FHIRResourceCategory.other + case .flag: + return FHIRResourceCategory.other + case .goal: + return FHIRResourceCategory.other + case .group: + return FHIRResourceCategory.other + case .healthcareService: + return FHIRResourceCategory.other + case .imagingObjectSelection: + return FHIRResourceCategory.other + case .imagingStudy: + return FHIRResourceCategory.other + case .immunization: + return FHIRResourceCategory.immunization + case .immunizationRecommendation: + return FHIRResourceCategory.immunization + case .implementationGuide: + return FHIRResourceCategory.other + case .list: + return FHIRResourceCategory.other + case .location: + return FHIRResourceCategory.other + case .media: + return FHIRResourceCategory.other + case .medication: + return FHIRResourceCategory.medication + case .medicationAdministration: + return FHIRResourceCategory.medication + case .medicationDispense: + return FHIRResourceCategory.medication + case .medicationOrder: + return FHIRResourceCategory.medication + case .medicationStatement: + return FHIRResourceCategory.medication + case .messageHeader: + return FHIRResourceCategory.other + case .namingSystem: + return FHIRResourceCategory.other + case .nutritionOrder: + return FHIRResourceCategory.other case .observation: - return .observation - case .procedure, - .procedureRequest: - return .procedure - default: - return .other + return FHIRResourceCategory.observation + case .operationDefinition: + return FHIRResourceCategory.other + case .operationOutcome: + return FHIRResourceCategory.other + case .order: + return FHIRResourceCategory.other + case .orderResponse: + return FHIRResourceCategory.other + case .organization: + return FHIRResourceCategory.other + case .parameters: + return FHIRResourceCategory.other + case .patient: + return FHIRResourceCategory.other + case .paymentNotice: + return FHIRResourceCategory.other + case .paymentReconciliation: + return FHIRResourceCategory.other + case .person: + return FHIRResourceCategory.other + case .practitioner: + return FHIRResourceCategory.other + case .procedure: + return FHIRResourceCategory.procedure + case .procedureRequest: + return FHIRResourceCategory.procedure + case .processRequest: + return FHIRResourceCategory.other + case .processResponse: + return FHIRResourceCategory.other + case .provenance: + return FHIRResourceCategory.other + case .questionnaire: + return FHIRResourceCategory.other + case .questionnaireResponse: + return FHIRResourceCategory.other + case .referralRequest: + return FHIRResourceCategory.other + case .relatedPerson: + return FHIRResourceCategory.other + case .resource: + return FHIRResourceCategory.other + case .riskAssessment: + return FHIRResourceCategory.other + case .schedule: + return FHIRResourceCategory.other + case .searchParameter: + return FHIRResourceCategory.other + case .slot: + return FHIRResourceCategory.other + case .specimen: + return FHIRResourceCategory.other + case .structureDefinition: + return FHIRResourceCategory.other + case .subscription: + return FHIRResourceCategory.other + case .substance: + return FHIRResourceCategory.other + case .supplyDelivery: + return FHIRResourceCategory.other + case .supplyRequest: + return FHIRResourceCategory.other + case .testScript: + return FHIRResourceCategory.other + case .valueSet: + return FHIRResourceCategory.other + case .visionPrescription: + return FHIRResourceCategory.other + case .unrecognized: + return FHIRResourceCategory.other } } } From 9e470cab4853572580ceed7fbb0ce566c80bd1e9 Mon Sep 17 00:00:00 2001 From: Joan Disho Date: Tue, 21 Jan 2025 11:28:31 +0100 Subject: [PATCH 8/8] Split test into different files --- Tests/SpeziFHIRTests/FHIRResourceTests.swift | 926 ------------------ .../FHIRResourceCategoryTests.swift | 98 ++ .../FHIRResourceDSTU2Mocks.swift | 196 ++++ .../FHIRResourceR4Mocks.swift | 326 ++++++ .../FHIRResourceSearchTests.swift | 57 ++ .../FHIRResourceTests/FHIRResourceTests.swift | 170 ++++ .../ResourceProxyDisplayNameTests.swift | 131 +++ 7 files changed, 978 insertions(+), 926 deletions(-) delete mode 100644 Tests/SpeziFHIRTests/FHIRResourceTests.swift create mode 100644 Tests/SpeziFHIRTests/FHIRResourceTests/FHIRResourceCategoryTests.swift create mode 100644 Tests/SpeziFHIRTests/FHIRResourceTests/FHIRResourceDSTU2Mocks.swift create mode 100644 Tests/SpeziFHIRTests/FHIRResourceTests/FHIRResourceR4Mocks.swift create mode 100644 Tests/SpeziFHIRTests/FHIRResourceTests/FHIRResourceSearchTests.swift create mode 100644 Tests/SpeziFHIRTests/FHIRResourceTests/FHIRResourceTests.swift create mode 100644 Tests/SpeziFHIRTests/FHIRResourceTests/ResourceProxyDisplayNameTests.swift diff --git a/Tests/SpeziFHIRTests/FHIRResourceTests.swift b/Tests/SpeziFHIRTests/FHIRResourceTests.swift deleted file mode 100644 index 56f29ec..0000000 --- a/Tests/SpeziFHIRTests/FHIRResourceTests.swift +++ /dev/null @@ -1,926 +0,0 @@ -// -// This source file is part of the Stanford Spezi open-source project -// -// SPDX-FileCopyrightText: 2024 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import ModelsDSTU2 -import ModelsR4 -@testable import SpeziFHIR -import XCTest - -// swiftlint:disable file_length -final class FHIRResourceTests: XCTestCase { - let testDate = Date(timeIntervalSince1970: 1735123266) - let calendar = Calendar.current - - func testModelsR4ResourceInitialization() throws { - let mockObservation = try ModelsR4Mocks.createObservation(date: testDate) - - let resource = FHIRResource( - resource: mockObservation, - displayName: "Test Observation" - ) - - XCTAssertEqual(resource.id, "observation-id") - XCTAssertEqual(resource.displayName, "Test Observation") - XCTAssertEqual(resource.resourceType, "Observation") - } - - func testModelsDSTU2ResourceInitialization() throws { - let mockObservation = try ModelsDSTU2Mocks.createObservation(date: testDate) - - let resource = FHIRResource( - resource: mockObservation, - displayName: "Test Observation" - ) - - XCTAssertEqual(resource.id, "observation-id") - XCTAssertEqual(resource.displayName, "Test Observation") - XCTAssertEqual(resource.resourceType, "Observation") - } - - func testModelsR4ResourceDates() throws { - let modelsR4Resources: [(ModelsR4.Resource, String)] = try [ - (ModelsR4Mocks.createCarePlan(date: testDate), "CarePlan"), - (ModelsR4Mocks.createCareTeam(date: testDate), "CareTeam"), - (ModelsR4Mocks.createClaim(date: testDate), "Claim"), - (ModelsR4Mocks.createCondition(date: testDate), "Condition"), - (ModelsR4Mocks.createDevice(date: testDate), "Device"), - (ModelsR4Mocks.createDiagnosticReport(date: testDate), "DiagnosticReport"), - (ModelsR4Mocks.createDocumentReference(date: testDate), "DocumentReference"), - (ModelsR4Mocks.createEncounter(date: testDate), "Encounter"), - (ModelsR4Mocks.createExplanationOfBenefit(date: testDate), "ExplanationOfBenefit"), - (ModelsR4Mocks.createImmunization(date: testDate), "Immunization"), - (ModelsR4Mocks.createMedicationRequest(date: testDate), "MedicationRequest"), - (ModelsR4Mocks.createMedicationAdministration(date: testDate), "MedicationAdministration"), - (ModelsR4Mocks.createObservation(date: testDate), "Observation"), - (ModelsR4Mocks.createProcedure(date: testDate), "Procedure"), - (ModelsR4Mocks.createPatient(date: testDate), "Patient"), - (ModelsR4Mocks.createProvenance(date: testDate), "Provenance"), - (ModelsR4Mocks.createSupplyDelivery(date: testDate), "SupplyDelivery") - ] - - for (resource, name) in modelsR4Resources { - let fhirResource = FHIRResource( - versionedResource: .r4(resource), - displayName: "Test \(name)" - ) - - assertEqualDates(fhirResource.date, testDate, name) - } - } - - func testModelsDSTU2ResourceDates() throws { - let modelsDSTU2Resources: [(ModelsDSTU2.Resource, String)] = try [ - (ModelsDSTU2Mocks.createObservation(date: testDate), "Observation"), - (ModelsDSTU2Mocks.createMedicationOrder(date: testDate), "MedicationOrder"), - (ModelsDSTU2Mocks.createMedicationStatement(date: testDate), "MedicationStatement"), - (ModelsDSTU2Mocks.createCondition(date: testDate), "Condition"), - (ModelsDSTU2Mocks.createProcedure(date: testDate), "Procedure"), - (ModelsDSTU2Mocks.createProcedure(date: testDate, usePeriod: false), "Procedure"), - (ModelsDSTU2Mocks.createProcedure(date: testDate, usePeriod: true), "Procedure with Period") - ] - - for (resource, name) in modelsDSTU2Resources { - let fhirResource = FHIRResource( - versionedResource: .dstu2(resource), - displayName: "Test \(name)" - ) - - XCTAssertEqual( - fhirResource.date?.timeIntervalSince1970, - testDate.timeIntervalSince1970, - "Date extraction failed for DSTU2 \(name)" - ) - } - } - - func testModelsR4JSON() throws { - let observation = ModelsR4.Observation( - code: CodeableConcept( - coding: [ - Coding(code: "test-code".asFHIRStringPrimitive()) - ] - ), - id: "test-id".asFHIRStringPrimitive(), - status: FHIRPrimitive(.final) - ) - - let resource = FHIRResource( - versionedResource: .r4(observation), - displayName: "Test" - ) - - let jsonString = resource.json(withConfiguration: []) - - let jsonData = jsonString.data(using: .utf8) ?? Data() - let decoder = JSONDecoder() - let decodedObservation = try decoder.decode(ModelsR4.Observation.self, from: jsonData) - - XCTAssertEqual(decodedObservation.id, observation.id) - XCTAssertEqual(decodedObservation.code, observation.code) - XCTAssertEqual(decodedObservation.status, observation.status) - } - - func testModelsDSTU2JSON() throws { - let observation = ModelsDSTU2.Observation( - code: CodeableConcept( - coding: [ - Coding(code: "test-code".asFHIRStringPrimitive()) - ] - ), - id: "test-id".asFHIRStringPrimitive(), - status: FHIRPrimitive(.final) - ) - - let resource = FHIRResource( - versionedResource: .dstu2(observation), - displayName: "Test" - ) - - let jsonString = resource.json(withConfiguration: []) - - let jsonData = jsonString.data(using: .utf8) ?? Data() - let decoder = JSONDecoder() - let decodedObservation = try decoder.decode(ModelsDSTU2.Observation.self, from: jsonData) - - XCTAssertEqual(decodedObservation.id, observation.id) - XCTAssertEqual(decodedObservation.code, observation.code) - XCTAssertEqual(decodedObservation.status, observation.status) - } - - func testMatchesDisplayName() throws { - let observation = try ModelsR4Mocks.createObservation(date: testDate) - - let resource = FHIRResource( - versionedResource: .r4(observation), - displayName: "Test Resource" - ) - - XCTAssertTrue(resource.matchesDisplayName(with: "test")) - XCTAssertTrue(resource.matchesDisplayName(with: "resource")) - XCTAssertTrue(resource.matchesDisplayName(with: " test ")) - XCTAssertTrue(resource.matchesDisplayName(with: "TEST")) - XCTAssertFalse(resource.matchesDisplayName(with: "xyz")) - XCTAssertFalse(resource.matchesDisplayName(with: "")) - } - - func testFilterByDisplayName() throws { - let observation = try ModelsR4Mocks.createObservation(date: testDate) - let patient = try ModelsR4Mocks.createPatient(date: testDate) - let medicationRequest = try ModelsR4Mocks.createMedicationRequest(date: testDate) - - let resource1 = FHIRResource( - versionedResource: .r4(observation), - displayName: "Test Resource1" - ) - - let resource2 = FHIRResource( - versionedResource: .r4(patient), - displayName: "Test Resource2" - ) - - let resource3 = FHIRResource( - versionedResource: .r4(medicationRequest), - displayName: "Test Resource3" - ) - - let resources = [resource1, resource2, resource3] - - XCTAssertEqual(resources.filterByDisplayName(with: "test").count, 3) - XCTAssertEqual(resources.filterByDisplayName(with: "resource1").count, 1) - XCTAssertEqual(resources.filterByDisplayName(with: "xyz").count, 0) - XCTAssertEqual(resources.filterByDisplayName(with: "").count, 3) - } - - func testConditionDisplayName() throws { - let mockCondition = try ModelsR4Mocks.createCondition(date: testDate) - - // Test with text value - mockCondition.code = CodeableConcept(text: "Hypertension") - let proxy = ResourceProxy(with: mockCondition) - XCTAssertEqual(proxy.displayName, "Hypertension") - - // Test with no code - mockCondition.code = nil - XCTAssertEqual(proxy.displayName, "Condition") - } - - func testDiagnosticReportDisplayName() throws { - let mockReport = try ModelsR4Mocks.createDiagnosticReport(date: testDate) - - // Test with display coding - mockReport.code.coding = [Coding(display: "Blood Test")] - let proxy = ResourceProxy(with: mockReport) - XCTAssertEqual(proxy.displayName, "Blood Test") - - // Test with no codings - mockReport.code.coding?.removeAll() - XCTAssertEqual(proxy.displayName, "DiagnosticReport") - } - - func testEncounterDisplayName() throws { - let mockEncounter = try ModelsR4Mocks.createEncounter(date: testDate) - - // Test with reason code - mockEncounter.reasonCode = [CodeableConcept(coding: [Coding(display: "Follow-up")])] - let proxy = ResourceProxy(with: mockEncounter) - XCTAssertEqual(proxy.displayName, "Follow-up") - - // Test with encounter type - mockEncounter.reasonCode = nil - mockEncounter.type = [CodeableConcept(coding: [Coding(display: "Office Visit")])] - XCTAssertEqual(proxy.displayName, "Office Visit") - - // Test with no type or reason - mockEncounter.type = nil - XCTAssertEqual(proxy.displayName, "Encounter") - } - - func testImmunizationDisplayName() throws { - let mockImmunization = try ModelsR4Mocks.createImmunization(date: testDate) - - // Test with vaccine text - mockImmunization.vaccineCode.text = "Flu Shot" - let proxy = ResourceProxy(with: mockImmunization) - XCTAssertEqual(proxy.displayName, "Flu Shot") - - // Test with no vaccine text - mockImmunization.vaccineCode.text = nil - XCTAssertEqual(proxy.displayName, "Immunization") - } - - func testMedicationRequestDisplayName() throws { - let mockMedRequest = try ModelsR4Mocks.createMedicationRequest(date: testDate) - - // Test with codeable concept text - if case let .codeableConcept(medicationCode) = mockMedRequest.medication { - medicationCode.text = "Aspirin" - } - let proxy = ResourceProxy(with: mockMedRequest) - XCTAssertEqual(proxy.displayName, "Aspirin") - - // Test with no text in codeable concept - if case let .codeableConcept(medicationCode) = mockMedRequest.medication { - medicationCode.text = nil - } - XCTAssertEqual(proxy.displayName, "MedicationRequest") - - // Test with reference instead of codeable concept - mockMedRequest.medication = .reference(Reference()) - XCTAssertEqual(proxy.displayName, "MedicationRequest") - } - - func testObservationDisplayName() throws { - let mockObservation = try ModelsR4Mocks.createObservation(date: testDate) - - // Test with code text - mockObservation.code.text = "Blood Pressure" - let proxy = ResourceProxy(with: mockObservation) - XCTAssertEqual(proxy.displayName, "Blood Pressure") - - // Test with no code text - mockObservation.code.text = nil - XCTAssertEqual(proxy.displayName, "Observation") - } - - func testProcedureDisplayName() throws { - let mockProcedure = try ModelsR4Mocks.createProcedure(date: testDate) - - // Test with code text - mockProcedure.code = CodeableConcept(text: "Hip Surgery") - let proxy = ResourceProxy(with: mockProcedure) - XCTAssertEqual(proxy.displayName, "Hip Surgery") - - // Test with no code - mockProcedure.code = nil - XCTAssertEqual(proxy.displayName, "Procedure") - } - - func testPatientDisplayName() throws { - let mockPatient = try ModelsR4Mocks.createPatient(date: testDate) - - // Test with name components - mockPatient.name = [HumanName(family: "Doe", given: ["John"])] - let proxy = ResourceProxy(with: mockPatient) - XCTAssertEqual(proxy.displayName, "JohnDoe") - - // Test with no name - mockPatient.name = nil - XCTAssertEqual(proxy.displayName, "Patient") - } - - func testModelsR4Categories() throws { - // Define test cases as tuples of (resource creator function, expected category) - let testCases: [(resource: () throws -> ModelsR4.Resource, category: FHIRResource.FHIRResourceCategory)] = [ - // Observation cases - ({ try ModelsR4Mocks.createObservation(date: self.testDate) }, .observation), - ({ ModelsR4Mocks.createObservationDefinition() }, .observation), - - // Single resource cases - ({ try ModelsR4Mocks.createEncounter(date: self.testDate) }, .encounter), - ({ try ModelsR4Mocks.createCondition(date: self.testDate) }, .condition), - ({ try ModelsR4Mocks.createDiagnosticReport(date: self.testDate) }, .diagnostic), - ({ try ModelsR4Mocks.createProcedure(date: self.testDate) }, .procedure), - ({ ModelsR4Mocks.createAllergyIntolerance() }, .allergyIntolerance), - - // Immunization cases - ({ try ModelsR4Mocks.createImmunization(date: self.testDate) }, .immunization), - ({ ModelsR4Mocks.createImmunizationEvaluation() }, .immunization), - ({ try ModelsR4Mocks.createImmunizationRecommendation(date: self.testDate) }, .immunization), - - // Medication cases - ({ ModelsR4Mocks.createMedication() }, .medication), - ({ try ModelsR4Mocks.createMedicationRequest(date: self.testDate) }, .medication), - ({ try ModelsR4Mocks.createMedicationAdministration(date: self.testDate) }, .medication), - ({ ModelsR4Mocks.createMedicationDispense() }, .medication), - ({ ModelsR4Mocks.createMedicationKnowledge() }, .medication), - ({ ModelsR4Mocks.createMedicationStatement() }, .medication) - ] - - try testCases.forEach { creator, expectedCategory in - let resource = try creator() - let fhirResource = FHIRResource(versionedResource: .r4(resource), displayName: "") - XCTAssertEqual(fhirResource.category, expectedCategory, "Failed for resource type: \(type(of: resource))") - } - } - - func testModelsDSTU2Categories() throws { - let testCases: [(resource: () throws -> ModelsDSTU2.Resource, category: FHIRResource.FHIRResourceCategory)] = [ - // Single resource cases - ({ ModelsDSTU2Mocks.createAllergyIntolerance() }, .allergyIntolerance), - ({ ModelsDSTU2Mocks.createEncounter() }, .encounter), - ({ try ModelsDSTU2Mocks.createObservation(date: self.testDate) }, .observation), - ({ try ModelsDSTU2Mocks.createCondition(date: self.testDate) }, .condition), - - // Diagnostic cases - ({ try ModelsDSTU2Mocks.createDiagnosticReport(date: self.testDate) }, .diagnostic), - ({ try ModelsDSTU2Mocks.createDiagnosticOrder(date: self.testDate) }, .diagnostic), - - // Procedure cases - ({ try ModelsDSTU2Mocks.createProcedure(date: self.testDate) }, .procedure), - ({ ModelsDSTU2Mocks.createProcedureRequest() }, .procedure), - - // Immunization cases - ({ try ModelsDSTU2Mocks.createImmunization(date: self.testDate) }, .immunization), - ({ ModelsDSTU2Mocks.createImmunizationRecommendation() }, .immunization), - - // Medication cases - ({ ModelsDSTU2Mocks.createMedication() }, .medication), - ({ try ModelsDSTU2Mocks.createMedicationOrder(date: self.testDate) }, .medication), - ({ try ModelsDSTU2Mocks.createMedicationAdministration(date: self.testDate) }, .medication), - ({ ModelsDSTU2Mocks.createMedicationDispense() }, .medication), - ({ try ModelsDSTU2Mocks.createMedicationStatement(date: self.testDate) }, .medication) - ] - - try testCases.forEach { creator, expectedCategory in - let resource = try creator() - let fhirResource = FHIRResource(versionedResource: .dstu2(resource), displayName: "") - XCTAssertEqual(fhirResource.category, expectedCategory, "Failed for resource type: \(type(of: resource))") - } - } - - @MainActor - func testCategoryKeyPaths() { - XCTAssertEqual(FHIRResource.FHIRResourceCategory.observation.storeKeyPath, \FHIRStore.observations) - XCTAssertEqual(FHIRResource.FHIRResourceCategory.encounter.storeKeyPath, \FHIRStore.encounters) - XCTAssertEqual(FHIRResource.FHIRResourceCategory.condition.storeKeyPath, \FHIRStore.conditions) - XCTAssertEqual(FHIRResource.FHIRResourceCategory.diagnostic.storeKeyPath, \FHIRStore.diagnostics) - XCTAssertEqual(FHIRResource.FHIRResourceCategory.procedure.storeKeyPath, \FHIRStore.procedures) - XCTAssertEqual(FHIRResource.FHIRResourceCategory.immunization.storeKeyPath, \FHIRStore.immunizations) - XCTAssertEqual(FHIRResource.FHIRResourceCategory.allergyIntolerance.storeKeyPath, \FHIRStore.allergyIntolerances) - XCTAssertEqual(FHIRResource.FHIRResourceCategory.medication.storeKeyPath, \FHIRStore.medications) - XCTAssertEqual(FHIRResource.FHIRResourceCategory.other.storeKeyPath, \FHIRStore.otherResources) - } - - private func assertEqualDates(_ resourceDate: Date?, _ expectedDate: Date, _ resourceName: String) { - guard let resourceDate = resourceDate else { - XCTFail("Date should not be nil for \(resourceName)") - return - } - - // For resources that use FHIRDate (like Patient birthDate), compare only date components - if resourceName == "Patient" { - let testComponents = calendar.dateComponents([.year, .month, .day], from: expectedDate) - let resourceComponents = calendar.dateComponents([.year, .month, .day], from: resourceDate) - - XCTAssertEqual( - testComponents.year, - resourceComponents.year, - "Year mismatch for \(resourceName)" - ) - XCTAssertEqual( - testComponents.month, - resourceComponents.month, - "Month mismatch for \(resourceName)" - ) - XCTAssertEqual( - testComponents.day, - resourceComponents.day, - "Day mismatch for \(resourceName)" - ) - } else { - // For other resources using DateTime/Instant, compare exact timestamps - XCTAssertEqual( - resourceDate.timeIntervalSince1970, - expectedDate.timeIntervalSince1970, - "Date extraction failed for \(resourceName)" - ) - } - } -} - -private enum ModelsR4Mocks { - static func createAllergyIntolerance() -> ModelsR4.AllergyIntolerance { - ModelsR4.AllergyIntolerance( - id: "allergy-intolerance-id".asFHIRStringPrimitive(), - patient: Reference(id: "patient-id".asFHIRStringPrimitive()) - ) - } - - static func createCarePlan(date: Date) throws -> ModelsR4.CarePlan { - let period = ModelsR4.Period() - period.start = try FHIRPrimitive(DateTime(date: date)) - - return ModelsR4.CarePlan( - id: "care-plan-id".asFHIRStringPrimitive(), - intent: FHIRPrimitive(.plan), - period: period, - status: FHIRPrimitive(.active), - subject: Reference(id: "patient-id".asFHIRStringPrimitive()) - ) - } - - static func createCareTeam(date: Date) throws -> ModelsR4.CareTeam { - let period = ModelsR4.Period() - period.start = try FHIRPrimitive(DateTime(date: date)) - - return ModelsR4.CareTeam( - id: "care-team-id".asFHIRStringPrimitive(), - period: period, - status: FHIRPrimitive(.active) - ) - } - - static func createClaim(date: Date) throws -> ModelsR4.Claim { - let period = ModelsR4.Period() - period.end = try FHIRPrimitive(DateTime(date: date)) - - return ModelsR4.Claim( - billablePeriod: period, - created: FHIRPrimitive(try DateTime(date: .now)), - id: "claim-id".asFHIRStringPrimitive(), - insurance: [ - ClaimInsurance( - coverage: Reference(id: "coverage-id".asFHIRStringPrimitive()), - focal: FHIRPrimitive(true), - sequence: FHIRPrimitive(1) - ) - ], - patient: Reference(id: "patient-id".asFHIRStringPrimitive()), - priority: CodeableConcept( - coding: [ - Coding(code: "normal".asFHIRStringPrimitive()) - ] - ), - provider: Reference(id: "provider-id".asFHIRStringPrimitive()), - status: FHIRPrimitive(.active), - type: CodeableConcept( - coding: [ - Coding(code: "type".asFHIRStringPrimitive()) - ] - ), - use: FHIRPrimitive(.claim) - ) - } - - static func createCondition(date: Date) throws -> ModelsR4.Condition { - ModelsR4.Condition( - id: "condition-id".asFHIRStringPrimitive(), - onset: .dateTime(FHIRPrimitive(try DateTime(date: date))), - subject: Reference(id: "patient-id".asFHIRStringPrimitive()) - ) - } - - static func createDevice(date: Date) throws -> ModelsR4.Device { - ModelsR4.Device( - id: "device-id".asFHIRStringPrimitive(), - manufactureDate: FHIRPrimitive(try DateTime(date: date)) - ) - } - - static func createDiagnosticReport(date: Date) throws -> ModelsR4.DiagnosticReport { - ModelsR4.DiagnosticReport( - code: CodeableConcept(coding: [Coding(code: "code".asFHIRStringPrimitive())]), - effective: .dateTime(FHIRPrimitive(try DateTime(date: date))), - id: "diagnostic-report-id".asFHIRStringPrimitive(), - status: FHIRPrimitive(.final) - ) - } - - static func createDocumentReference(date: Date) throws -> ModelsR4.DocumentReference { - let content = ModelsR4.DocumentReferenceContent( - attachment: Attachment( - contentType: "text/plain".asFHIRStringPrimitive() - ) - ) - - return ModelsR4.DocumentReference( - content: [content], - date: FHIRPrimitive(try Instant(date: date)), - id: "document-reference-id".asFHIRStringPrimitive(), - status: FHIRPrimitive(.current) - ) - } - - static func createEncounter(date: Date) throws -> ModelsR4.Encounter { - let period = ModelsR4.Period() - period.end = try FHIRPrimitive(DateTime(date: date)) - - return ModelsR4.Encounter( - class: Coding( - code: "AMB".asFHIRStringPrimitive(), - system: FHIRPrimitive("http://terminology.hl7.org/CodeSystem/v3-ActCode") - ), - id: "encounter-id".asFHIRStringPrimitive(), - period: period, - status: FHIRPrimitive(.finished) - ) - } - - static func createExplanationOfBenefit(date: Date) throws -> ModelsR4.ExplanationOfBenefit { - let period = ModelsR4.Period() - period.end = try FHIRPrimitive(DateTime(date: date)) - - return ModelsR4.ExplanationOfBenefit( - billablePeriod: period, - created: FHIRPrimitive(try DateTime(date: .now)), - id: "explanation-of-benefit-id".asFHIRStringPrimitive(), - insurance: [ - ExplanationOfBenefitInsurance( - coverage: Reference(id: "coverage-id".asFHIRStringPrimitive()), - focal: FHIRPrimitive(true) - ) - ], - insurer: Reference(id: "insurer-id".asFHIRStringPrimitive()), - outcome: FHIRPrimitive(.complete), - patient: Reference(id: "patient-id".asFHIRStringPrimitive()), - provider: Reference(id: "provider-id".asFHIRStringPrimitive()), - status: FHIRPrimitive(.active), - type: CodeableConcept( - coding: [ - Coding(code: "type".asFHIRStringPrimitive()) - ] - ), - use: FHIRPrimitive(.claim) - ) - } - - static func createImmunization(date: Date) throws -> ModelsR4.Immunization { - ModelsR4.Immunization( - id: "immunization-id".asFHIRStringPrimitive(), - occurrence: .dateTime(FHIRPrimitive(try DateTime(date: date))), - patient: Reference(id: "patient-id".asFHIRStringPrimitive()), - status: FHIRPrimitive(.completed), - vaccineCode: CodeableConcept( - coding: [ - Coding(code: "vaccine-code".asFHIRStringPrimitive()) - ] - ) - ) - } - - static func createImmunizationEvaluation() -> ModelsR4.ImmunizationEvaluation { - ModelsR4.ImmunizationEvaluation( - doseStatus: CodeableConcept( - coding: [ - Coding(code: "dose-status".asFHIRStringPrimitive()) - ] - ), - id: "immunization-eval-id".asFHIRStringPrimitive(), - immunizationEvent: Reference(id: "event-id".asFHIRStringPrimitive()), - patient: Reference(id: "patient-id".asFHIRStringPrimitive()), - status: FHIRPrimitive(.completed), - targetDisease: CodeableConcept( - coding: [ - Coding(code: "targe-disease".asFHIRStringPrimitive()) - ] - ) - ) - } - - static func createImmunizationRecommendation(date: Date) throws -> ModelsR4.ImmunizationRecommendation { - ModelsR4.ImmunizationRecommendation( - date: FHIRPrimitive(try DateTime(date: date)), - id: "immunization-recommendation-id".asFHIRStringPrimitive(), - patient: Reference(id: "patient-id".asFHIRStringPrimitive()), - recommendation: [] - ) - } - - static func createMedication() -> ModelsR4.Medication { - ModelsR4.Medication(id: "medication-id".asFHIRStringPrimitive()) - } - - static func createMedicationDispense() -> ModelsR4.MedicationDispense { - ModelsR4.MedicationDispense( - id: "medication-dispense-id".asFHIRStringPrimitive(), - medication: .codeableConcept( - CodeableConcept(coding: [ - Coding(code: "med-code".asFHIRStringPrimitive()) - ]) - ), - status: FHIRPrimitive(.completed) - ) - } - - static func createMedicationKnowledge() -> ModelsR4.MedicationKnowledge { - ModelsR4.MedicationKnowledge(id: "medication-knowledge-id".asFHIRStringPrimitive()) - } - - static func createMedicationRequest(date: Date) throws -> ModelsR4.MedicationRequest { - ModelsR4.MedicationRequest( - authoredOn: FHIRPrimitive(try DateTime(date: date)), - id: "medication-request-id".asFHIRStringPrimitive(), - intent: FHIRPrimitive(.order), - medication: .codeableConcept( - CodeableConcept(coding: [ - Coding(code: "med-code".asFHIRStringPrimitive()) - ]) - ), - status: FHIRPrimitive(.active), - subject: Reference(id: "patient-id".asFHIRStringPrimitive()) - ) - } - - static func createMedicationAdministration(date: Date) throws -> ModelsR4.MedicationAdministration { - ModelsR4.MedicationAdministration( - effective: .dateTime(FHIRPrimitive(try DateTime(date: date))), - id: "medication-administration-id".asFHIRStringPrimitive(), - medication: .codeableConcept( - CodeableConcept(coding: [ - Coding(code: "med-code".asFHIRStringPrimitive()) - ]) - ), - status: FHIRPrimitive(.completed), - subject: Reference(id: "patient-id".asFHIRStringPrimitive()) - ) - } - - static func createMedicationStatement() -> ModelsR4.MedicationStatement { - ModelsR4.MedicationStatement( - id: "medication-statement-id".asFHIRStringPrimitive(), - medication: .codeableConcept( - CodeableConcept(coding: [ - Coding(code: "med-code".asFHIRStringPrimitive()) - ]) - ), - status: FHIRPrimitive(.completed), - subject: Reference(id: "patient-id".asFHIRStringPrimitive()) - ) - } - - static func createObservation(date: Date) throws -> ModelsR4.Observation { - ModelsR4.Observation( - code: CodeableConcept(coding: [Coding(code: "code".asFHIRStringPrimitive())]), - id: "observation-id".asFHIRStringPrimitive(), - issued: FHIRPrimitive(try Instant(date: date)), - status: FHIRPrimitive(.final) - ) - } - - static func createObservationDefinition() -> ModelsR4.ObservationDefinition { - ModelsR4.ObservationDefinition( - code: CodeableConcept(coding: [Coding(code: "code".asFHIRStringPrimitive())]), - id: "observation-definition-id".asFHIRStringPrimitive() - ) - } - - static func createProcedure(date: Date) throws -> ModelsR4.Procedure { - ModelsR4.Procedure( - id: "procedure-id".asFHIRStringPrimitive(), - performed: .dateTime(FHIRPrimitive(try DateTime(date: date))), - status: FHIRPrimitive(.completed), - subject: Reference(id: "patient-id".asFHIRStringPrimitive()) - ) - } - - static func createPatient(date: Date) throws -> ModelsR4.Patient { - ModelsR4.Patient( - birthDate: FHIRPrimitive(try FHIRDate(date: date)), - id: "patient-id".asFHIRStringPrimitive() - ) - } - - static func createProvenance(date: Date) throws -> ModelsR4.Provenance { - ModelsR4.Provenance( - agent: [ - ProvenanceAgent( - type: CodeableConcept( - coding: [Coding(code: "agent-type".asFHIRStringPrimitive())] - ), - who: Reference(id: "agent-id".asFHIRStringPrimitive()) - ) - ], - id: "provenance-id".asFHIRStringPrimitive(), - recorded: FHIRPrimitive(try Instant(date: date)), - target: [ - Reference(id: "target-id".asFHIRStringPrimitive()) - ] - ) - } - - static func createSupplyDelivery(date: Date) throws -> ModelsR4.SupplyDelivery { - ModelsR4.SupplyDelivery( - id: "supply-delivery-id".asFHIRStringPrimitive(), - occurrence: .dateTime(FHIRPrimitive(try DateTime(date: date))), - status: FHIRPrimitive(.completed) - ) - } -} - - -private enum ModelsDSTU2Mocks { - static func createAllergyIntolerance() -> ModelsDSTU2.AllergyIntolerance { - ModelsDSTU2.AllergyIntolerance( - id: "allergy-intolerance-id".asFHIRStringPrimitive(), - patient: Reference(id: "patient-id".asFHIRStringPrimitive()), - substance: CodeableConcept( - coding: [ - Coding(code: "code".asFHIRStringPrimitive()) - ] - ) - ) - } - - static func createDiagnosticOrder(date: Date) throws -> ModelsDSTU2.DiagnosticOrder { - ModelsDSTU2.DiagnosticOrder( - id: "diagnostic-order-id".asFHIRStringPrimitive(), - subject: Reference(id: "patient-id".asFHIRStringPrimitive()) - ) - } - - static func createDiagnosticReport(date: Date) throws -> ModelsDSTU2.DiagnosticReport { - ModelsDSTU2.DiagnosticReport( - code: CodeableConcept( - coding: [ - Coding(code: "code".asFHIRStringPrimitive()) - ] - ), - effective: .dateTime(FHIRPrimitive(try DateTime(date: date))), - id: "diagnostic-report-id".asFHIRStringPrimitive(), - issued: FHIRPrimitive(try Instant(date: date)), - performer: Reference(id: "patient-id".asFHIRStringPrimitive()), - status: FHIRPrimitive(.final), - subject: Reference(id: "patient-id".asFHIRStringPrimitive())) - } - - static func createObservation(date: Date) throws -> ModelsDSTU2.Observation { - ModelsDSTU2.Observation( - code: CodeableConcept( - coding: [ - Coding(code: "code".asFHIRStringPrimitive()) - ] - ), - id: "observation-id".asFHIRStringPrimitive(), - issued: FHIRPrimitive(try Instant(date: date)), - status: FHIRPrimitive(.final) - ) - } - - static func createImmunization(date: Date) throws -> ModelsDSTU2.Immunization { - ModelsDSTU2.Immunization( - id: "immunization-id".asFHIRStringPrimitive(), - patient: Reference(id: "patient-id".asFHIRStringPrimitive()), - reported: FHIRPrimitive(true), - status: FHIRPrimitive(.completed), - vaccineCode: CodeableConcept( - coding: [ - Coding(code: "code".asFHIRStringPrimitive()) - ] - ), - wasNotGiven: FHIRPrimitive(true) - ) - } - - static func createImmunizationRecommendation() -> ModelsDSTU2.ImmunizationRecommendation { - ModelsDSTU2.ImmunizationRecommendation( - id: "immunization-recommendation-id".asFHIRStringPrimitive(), - patient: Reference(id: "patient-id".asFHIRStringPrimitive()), - recommendation: [] - ) - } - - static func createMedication() -> ModelsDSTU2.Medication { - ModelsDSTU2.Medication(id: "medication-id".asFHIRStringPrimitive()) - } - - static func createMedicationAdministration(date: Date) throws -> ModelsDSTU2.MedicationAdministration { - ModelsDSTU2.MedicationAdministration( - effectiveTime: .dateTime(FHIRPrimitive(try DateTime(date: date))), - id: "medication-administration-id".asFHIRStringPrimitive(), - medication: .codeableConcept( - CodeableConcept(coding: [ - Coding(code: "med-code".asFHIRStringPrimitive()) - ]) - ), - patient: Reference(id: "patient-id".asFHIRStringPrimitive()), - status: FHIRPrimitive(.completed) - ) - } - - static func createMedicationDispense() -> ModelsDSTU2.MedicationDispense { - ModelsDSTU2.MedicationDispense( - id: "medication-dispense-id".asFHIRStringPrimitive(), - medication: .codeableConcept( - CodeableConcept(coding: [ - Coding(code: "med-code".asFHIRStringPrimitive()) - ]) - ), - status: FHIRPrimitive(.completed) - ) - } - - static func createMedicationOrder(date: Date) throws -> ModelsDSTU2.MedicationOrder { - ModelsDSTU2.MedicationOrder( - dateWritten: FHIRPrimitive(try DateTime(date: date)), - id: "medication-order-id".asFHIRStringPrimitive(), - medication: .codeableConcept( - CodeableConcept(coding: [ - Coding(code: "med-code".asFHIRStringPrimitive()) - ]) - ), - status: FHIRPrimitive(.active) - ) - } - - static func createMedicationStatement(date: Date) throws -> ModelsDSTU2.MedicationStatement { - ModelsDSTU2.MedicationStatement( - effective: .dateTime(FHIRPrimitive(try DateTime(date: date))), - id: "medication-statement-id".asFHIRStringPrimitive(), - medication: .codeableConcept( - CodeableConcept(coding: [ - Coding(code: "med-code".asFHIRStringPrimitive()) - ]) - ), - patient: Reference(id: "patient-id".asFHIRStringPrimitive()), - status: FHIRPrimitive(.active) - ) - } - - static func createCondition(date: Date) throws -> ModelsDSTU2.Condition { - ModelsDSTU2.Condition( - code: CodeableConcept( - coding: [ - Coding(code: "condition-code".asFHIRStringPrimitive()) - ] - ), - id: "condition-id".asFHIRStringPrimitive(), - onset: .dateTime(FHIRPrimitive(try DateTime(date: date))), - patient: Reference(id: "patient-id".asFHIRStringPrimitive()), - verificationStatus: FHIRPrimitive(.confirmed) - ) - } - - static func createEncounter() -> ModelsDSTU2.Encounter { - ModelsDSTU2.Encounter(id: "encounter-id".asFHIRStringPrimitive(), status: FHIRPrimitive(.finished)) - } - - static func createProcedure(date: Date, usePeriod: Bool = false) throws -> ModelsDSTU2.Procedure { - let period = ModelsDSTU2.Period() - period.end = try FHIRPrimitive(DateTime(date: date)) - - let performed: ModelsDSTU2.Procedure.PerformedX = usePeriod ? - .period(period) : - .dateTime(FHIRPrimitive(try DateTime(date: date))) - - return ModelsDSTU2.Procedure( - code: CodeableConcept( - coding: [ - Coding(code: "procedure-code".asFHIRStringPrimitive()) - ] - ), - id: "procedure-id".asFHIRStringPrimitive(), - performed: performed, - status: FHIRPrimitive(.completed), - subject: Reference(id: "patient-id".asFHIRStringPrimitive()) - ) - } - - static func createProcedureRequest() -> ModelsDSTU2.ProcedureRequest { - ModelsDSTU2.ProcedureRequest( - code: CodeableConcept( - coding: [ - Coding(code: "procedure-code".asFHIRStringPrimitive()) - ] - ), - id: "procedure-id".asFHIRStringPrimitive(), - performer: Reference(id: "performer-id".asFHIRStringPrimitive()), - status: FHIRPrimitive(.completed), - subject: Reference(id: "patient-id".asFHIRStringPrimitive()) - ) - } -} diff --git a/Tests/SpeziFHIRTests/FHIRResourceTests/FHIRResourceCategoryTests.swift b/Tests/SpeziFHIRTests/FHIRResourceTests/FHIRResourceCategoryTests.swift new file mode 100644 index 0000000..9b0addf --- /dev/null +++ b/Tests/SpeziFHIRTests/FHIRResourceTests/FHIRResourceCategoryTests.swift @@ -0,0 +1,98 @@ +// +// This source file is part of the Stanford Spezi open-source project +// +// SPDX-FileCopyrightText: 2025 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import ModelsDSTU2 +import ModelsR4 +@testable import SpeziFHIR +import XCTest + + +extension FHIRResourceTests { + func testModelsR4Categories() throws { + // Define test cases as tuples of (resource creator function, expected category) + let testCases: [(resource: () throws -> ModelsR4.Resource, category: FHIRResource.FHIRResourceCategory)] = [ + // Observation cases + ({ try ModelsR4Mocks.createObservation(date: self.testDate) }, .observation), + ({ ModelsR4Mocks.createObservationDefinition() }, .observation), + + // Single resource cases + ({ try ModelsR4Mocks.createEncounter(date: self.testDate) }, .encounter), + ({ try ModelsR4Mocks.createCondition(date: self.testDate) }, .condition), + ({ try ModelsR4Mocks.createDiagnosticReport(date: self.testDate) }, .diagnostic), + ({ try ModelsR4Mocks.createProcedure(date: self.testDate) }, .procedure), + ({ ModelsR4Mocks.createAllergyIntolerance() }, .allergyIntolerance), + + // Immunization cases + ({ try ModelsR4Mocks.createImmunization(date: self.testDate) }, .immunization), + ({ ModelsR4Mocks.createImmunizationEvaluation() }, .immunization), + ({ try ModelsR4Mocks.createImmunizationRecommendation(date: self.testDate) }, .immunization), + + // Medication cases + ({ ModelsR4Mocks.createMedication() }, .medication), + ({ try ModelsR4Mocks.createMedicationRequest(date: self.testDate) }, .medication), + ({ try ModelsR4Mocks.createMedicationAdministration(date: self.testDate) }, .medication), + ({ ModelsR4Mocks.createMedicationDispense() }, .medication), + ({ ModelsR4Mocks.createMedicationKnowledge() }, .medication), + ({ ModelsR4Mocks.createMedicationStatement() }, .medication) + ] + + try testCases.forEach { creator, expectedCategory in + let resource = try creator() + let fhirResource = FHIRResource(versionedResource: .r4(resource), displayName: "") + XCTAssertEqual(fhirResource.category, expectedCategory, "Failed for resource type: \(type(of: resource))") + } + } + + func testModelsDSTU2Categories() throws { + let testCases: [(resource: () throws -> ModelsDSTU2.Resource, category: FHIRResource.FHIRResourceCategory)] = [ + // Single resource cases + ({ ModelsDSTU2Mocks.createAllergyIntolerance() }, .allergyIntolerance), + ({ ModelsDSTU2Mocks.createEncounter() }, .encounter), + ({ try ModelsDSTU2Mocks.createObservation(date: self.testDate) }, .observation), + ({ try ModelsDSTU2Mocks.createCondition(date: self.testDate) }, .condition), + + // Diagnostic cases + ({ try ModelsDSTU2Mocks.createDiagnosticReport(date: self.testDate) }, .diagnostic), + ({ try ModelsDSTU2Mocks.createDiagnosticOrder(date: self.testDate) }, .diagnostic), + + // Procedure cases + ({ try ModelsDSTU2Mocks.createProcedure(date: self.testDate) }, .procedure), + ({ ModelsDSTU2Mocks.createProcedureRequest() }, .procedure), + + // Immunization cases + ({ try ModelsDSTU2Mocks.createImmunization(date: self.testDate) }, .immunization), + ({ ModelsDSTU2Mocks.createImmunizationRecommendation() }, .immunization), + + // Medication cases + ({ ModelsDSTU2Mocks.createMedication() }, .medication), + ({ try ModelsDSTU2Mocks.createMedicationOrder(date: self.testDate) }, .medication), + ({ try ModelsDSTU2Mocks.createMedicationAdministration(date: self.testDate) }, .medication), + ({ ModelsDSTU2Mocks.createMedicationDispense() }, .medication), + ({ try ModelsDSTU2Mocks.createMedicationStatement(date: self.testDate) }, .medication) + ] + + try testCases.forEach { creator, expectedCategory in + let resource = try creator() + let fhirResource = FHIRResource(versionedResource: .dstu2(resource), displayName: "") + XCTAssertEqual(fhirResource.category, expectedCategory, "Failed for resource type: \(type(of: resource))") + } + } + + @MainActor + func testCategoryKeyPaths() { + XCTAssertEqual(FHIRResource.FHIRResourceCategory.observation.storeKeyPath, \FHIRStore.observations) + XCTAssertEqual(FHIRResource.FHIRResourceCategory.encounter.storeKeyPath, \FHIRStore.encounters) + XCTAssertEqual(FHIRResource.FHIRResourceCategory.condition.storeKeyPath, \FHIRStore.conditions) + XCTAssertEqual(FHIRResource.FHIRResourceCategory.diagnostic.storeKeyPath, \FHIRStore.diagnostics) + XCTAssertEqual(FHIRResource.FHIRResourceCategory.procedure.storeKeyPath, \FHIRStore.procedures) + XCTAssertEqual(FHIRResource.FHIRResourceCategory.immunization.storeKeyPath, \FHIRStore.immunizations) + XCTAssertEqual(FHIRResource.FHIRResourceCategory.allergyIntolerance.storeKeyPath, \FHIRStore.allergyIntolerances) + XCTAssertEqual(FHIRResource.FHIRResourceCategory.medication.storeKeyPath, \FHIRStore.medications) + XCTAssertEqual(FHIRResource.FHIRResourceCategory.other.storeKeyPath, \FHIRStore.otherResources) + } +} diff --git a/Tests/SpeziFHIRTests/FHIRResourceTests/FHIRResourceDSTU2Mocks.swift b/Tests/SpeziFHIRTests/FHIRResourceTests/FHIRResourceDSTU2Mocks.swift new file mode 100644 index 0000000..30fd2fa --- /dev/null +++ b/Tests/SpeziFHIRTests/FHIRResourceTests/FHIRResourceDSTU2Mocks.swift @@ -0,0 +1,196 @@ +// +// This source file is part of the Stanford Spezi open-source project +// +// SPDX-FileCopyrightText: 2025 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import Foundation +import ModelsDSTU2 + + +extension FHIRResourceTests { + enum ModelsDSTU2Mocks { + static func createAllergyIntolerance() -> ModelsDSTU2.AllergyIntolerance { + ModelsDSTU2.AllergyIntolerance( + id: "allergy-intolerance-id".asFHIRStringPrimitive(), + patient: Reference(id: "patient-id".asFHIRStringPrimitive()), + substance: CodeableConcept( + coding: [ + Coding(code: "code".asFHIRStringPrimitive()) + ] + ) + ) + } + + static func createDiagnosticOrder(date: Date) throws -> ModelsDSTU2.DiagnosticOrder { + ModelsDSTU2.DiagnosticOrder( + id: "diagnostic-order-id".asFHIRStringPrimitive(), + subject: Reference(id: "patient-id".asFHIRStringPrimitive()) + ) + } + + static func createDiagnosticReport(date: Date) throws -> ModelsDSTU2.DiagnosticReport { + ModelsDSTU2.DiagnosticReport( + code: CodeableConcept( + coding: [ + Coding(code: "code".asFHIRStringPrimitive()) + ] + ), + effective: .dateTime(FHIRPrimitive(try DateTime(date: date))), + id: "diagnostic-report-id".asFHIRStringPrimitive(), + issued: FHIRPrimitive(try Instant(date: date)), + performer: Reference(id: "patient-id".asFHIRStringPrimitive()), + status: FHIRPrimitive(.final), + subject: Reference(id: "patient-id".asFHIRStringPrimitive()) + ) + } + + static func createObservation(date: Date) throws -> ModelsDSTU2.Observation { + ModelsDSTU2.Observation( + code: CodeableConcept( + coding: [ + Coding(code: "code".asFHIRStringPrimitive()) + ] + ), + id: "observation-id".asFHIRStringPrimitive(), + issued: FHIRPrimitive(try Instant(date: date)), + status: FHIRPrimitive(.final) + ) + } + + static func createImmunization(date: Date) throws -> ModelsDSTU2.Immunization { + ModelsDSTU2.Immunization( + id: "immunization-id".asFHIRStringPrimitive(), + patient: Reference(id: "patient-id".asFHIRStringPrimitive()), + reported: FHIRPrimitive(true), + status: FHIRPrimitive(.completed), + vaccineCode: CodeableConcept( + coding: [ + Coding(code: "code".asFHIRStringPrimitive()) + ] + ), + wasNotGiven: FHIRPrimitive(true) + ) + } + + static func createImmunizationRecommendation() -> ModelsDSTU2.ImmunizationRecommendation { + ModelsDSTU2.ImmunizationRecommendation( + id: "immunization-recommendation-id".asFHIRStringPrimitive(), + patient: Reference(id: "patient-id".asFHIRStringPrimitive()), + recommendation: [] + ) + } + + static func createMedication() -> ModelsDSTU2.Medication { + ModelsDSTU2.Medication(id: "medication-id".asFHIRStringPrimitive()) + } + + static func createMedicationAdministration(date: Date) throws -> ModelsDSTU2.MedicationAdministration { + ModelsDSTU2.MedicationAdministration( + effectiveTime: .dateTime(FHIRPrimitive(try DateTime(date: date))), + id: "medication-administration-id".asFHIRStringPrimitive(), + medication: .codeableConcept( + CodeableConcept(coding: [ + Coding(code: "med-code".asFHIRStringPrimitive()) + ]) + ), + patient: Reference(id: "patient-id".asFHIRStringPrimitive()), + status: FHIRPrimitive(.completed) + ) + } + + static func createMedicationDispense() -> ModelsDSTU2.MedicationDispense { + ModelsDSTU2.MedicationDispense( + id: "medication-dispense-id".asFHIRStringPrimitive(), + medication: .codeableConcept( + CodeableConcept(coding: [ + Coding(code: "med-code".asFHIRStringPrimitive()) + ]) + ), + status: FHIRPrimitive(.completed) + ) + } + + static func createMedicationOrder(date: Date) throws -> ModelsDSTU2.MedicationOrder { + ModelsDSTU2.MedicationOrder( + dateWritten: FHIRPrimitive(try DateTime(date: date)), + id: "medication-order-id".asFHIRStringPrimitive(), + medication: .codeableConcept( + CodeableConcept(coding: [ + Coding(code: "med-code".asFHIRStringPrimitive()) + ]) + ), + status: FHIRPrimitive(.active) + ) + } + + static func createMedicationStatement(date: Date) throws -> ModelsDSTU2.MedicationStatement { + ModelsDSTU2.MedicationStatement( + effective: .dateTime(FHIRPrimitive(try DateTime(date: date))), + id: "medication-statement-id".asFHIRStringPrimitive(), + medication: .codeableConcept( + CodeableConcept(coding: [ + Coding(code: "med-code".asFHIRStringPrimitive()) + ]) + ), + patient: Reference(id: "patient-id".asFHIRStringPrimitive()), + status: FHIRPrimitive(.active) + ) + } + + static func createCondition(date: Date) throws -> ModelsDSTU2.Condition { + ModelsDSTU2.Condition( + code: CodeableConcept( + coding: [ + Coding(code: "condition-code".asFHIRStringPrimitive()) + ] + ), + id: "condition-id".asFHIRStringPrimitive(), + onset: .dateTime(FHIRPrimitive(try DateTime(date: date))), + patient: Reference(id: "patient-id".asFHIRStringPrimitive()), + verificationStatus: FHIRPrimitive(.confirmed) + ) + } + + static func createEncounter() -> ModelsDSTU2.Encounter { + ModelsDSTU2.Encounter(id: "encounter-id".asFHIRStringPrimitive(), status: FHIRPrimitive(.finished)) + } + + static func createProcedure(date: Date, usePeriod: Bool = false) throws -> ModelsDSTU2.Procedure { + let period = ModelsDSTU2.Period() + period.end = try FHIRPrimitive(DateTime(date: date)) + + let performed: ModelsDSTU2.Procedure.PerformedX = usePeriod ? + .period(period) : + .dateTime(FHIRPrimitive(try DateTime(date: date))) + + return ModelsDSTU2.Procedure( + code: CodeableConcept( + coding: [ + Coding(code: "procedure-code".asFHIRStringPrimitive()) + ] + ), + id: "procedure-id".asFHIRStringPrimitive(), + performed: performed, + status: FHIRPrimitive(.completed), + subject: Reference(id: "patient-id".asFHIRStringPrimitive()) + ) + } + + static func createProcedureRequest() -> ModelsDSTU2.ProcedureRequest { + ModelsDSTU2.ProcedureRequest( + code: CodeableConcept( + coding: [ + Coding(code: "procedure-code".asFHIRStringPrimitive()) + ] + ), + id: "procedure-id".asFHIRStringPrimitive(), + performer: Reference(id: "performer-id".asFHIRStringPrimitive()), + status: FHIRPrimitive(.completed), + subject: Reference(id: "patient-id".asFHIRStringPrimitive()) + ) + } + } +} diff --git a/Tests/SpeziFHIRTests/FHIRResourceTests/FHIRResourceR4Mocks.swift b/Tests/SpeziFHIRTests/FHIRResourceTests/FHIRResourceR4Mocks.swift new file mode 100644 index 0000000..743e7db --- /dev/null +++ b/Tests/SpeziFHIRTests/FHIRResourceTests/FHIRResourceR4Mocks.swift @@ -0,0 +1,326 @@ +// +// This source file is part of the Stanford Spezi open-source project +// +// SPDX-FileCopyrightText: 2025 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import Foundation +import ModelsR4 + + +extension FHIRResourceTests { + enum ModelsR4Mocks { // swiftlint:disable:this type_body_length + static func createAllergyIntolerance() -> ModelsR4.AllergyIntolerance { + ModelsR4.AllergyIntolerance( + id: "allergy-intolerance-id".asFHIRStringPrimitive(), + patient: Reference(id: "patient-id".asFHIRStringPrimitive()) + ) + } + + static func createCarePlan(date: Date) throws -> ModelsR4.CarePlan { + let period = ModelsR4.Period() + period.start = try FHIRPrimitive(DateTime(date: date)) + + return ModelsR4.CarePlan( + id: "care-plan-id".asFHIRStringPrimitive(), + intent: FHIRPrimitive(.plan), + period: period, + status: FHIRPrimitive(.active), + subject: Reference(id: "patient-id".asFHIRStringPrimitive()) + ) + } + + static func createCareTeam(date: Date) throws -> ModelsR4.CareTeam { + let period = ModelsR4.Period() + period.start = try FHIRPrimitive(DateTime(date: date)) + + return ModelsR4.CareTeam( + id: "care-team-id".asFHIRStringPrimitive(), + period: period, + status: FHIRPrimitive(.active) + ) + } + + static func createClaim(date: Date) throws -> ModelsR4.Claim { + let period = ModelsR4.Period() + period.end = try FHIRPrimitive(DateTime(date: date)) + + return ModelsR4.Claim( + billablePeriod: period, + created: FHIRPrimitive(try DateTime(date: .now)), + id: "claim-id".asFHIRStringPrimitive(), + insurance: [ + ClaimInsurance( + coverage: Reference(id: "coverage-id".asFHIRStringPrimitive()), + focal: FHIRPrimitive(true), + sequence: FHIRPrimitive(1) + ) + ], + patient: Reference(id: "patient-id".asFHIRStringPrimitive()), + priority: CodeableConcept( + coding: [ + Coding(code: "normal".asFHIRStringPrimitive()) + ] + ), + provider: Reference(id: "provider-id".asFHIRStringPrimitive()), + status: FHIRPrimitive(.active), + type: CodeableConcept( + coding: [ + Coding(code: "type".asFHIRStringPrimitive()) + ] + ), + use: FHIRPrimitive(.claim) + ) + } + + static func createCondition(date: Date) throws -> ModelsR4.Condition { + ModelsR4.Condition( + id: "condition-id".asFHIRStringPrimitive(), + onset: .dateTime(FHIRPrimitive(try DateTime(date: date))), + subject: Reference(id: "patient-id".asFHIRStringPrimitive()) + ) + } + + static func createDevice(date: Date) throws -> ModelsR4.Device { + ModelsR4.Device( + id: "device-id".asFHIRStringPrimitive(), + manufactureDate: FHIRPrimitive(try DateTime(date: date)) + ) + } + + static func createDiagnosticReport(date: Date) throws -> ModelsR4.DiagnosticReport { + ModelsR4.DiagnosticReport( + code: CodeableConcept(coding: [Coding(code: "code".asFHIRStringPrimitive())]), + effective: .dateTime(FHIRPrimitive(try DateTime(date: date))), + id: "diagnostic-report-id".asFHIRStringPrimitive(), + status: FHIRPrimitive(.final) + ) + } + + static func createDocumentReference(date: Date) throws -> ModelsR4.DocumentReference { + let content = ModelsR4.DocumentReferenceContent( + attachment: Attachment( + contentType: "text/plain".asFHIRStringPrimitive() + ) + ) + + return ModelsR4.DocumentReference( + content: [content], + date: FHIRPrimitive(try Instant(date: date)), + id: "document-reference-id".asFHIRStringPrimitive(), + status: FHIRPrimitive(.current) + ) + } + + static func createEncounter(date: Date) throws -> ModelsR4.Encounter { + let period = ModelsR4.Period() + period.end = try FHIRPrimitive(DateTime(date: date)) + + return ModelsR4.Encounter( + class: Coding( + code: "AMB".asFHIRStringPrimitive(), + system: FHIRPrimitive("http://terminology.hl7.org/CodeSystem/v3-ActCode") + ), + id: "encounter-id".asFHIRStringPrimitive(), + period: period, + status: FHIRPrimitive(.finished) + ) + } + + static func createExplanationOfBenefit(date: Date) throws -> ModelsR4.ExplanationOfBenefit { + let period = ModelsR4.Period() + period.end = try FHIRPrimitive(DateTime(date: date)) + + return ModelsR4.ExplanationOfBenefit( + billablePeriod: period, + created: FHIRPrimitive(try DateTime(date: .now)), + id: "explanation-of-benefit-id".asFHIRStringPrimitive(), + insurance: [ + ExplanationOfBenefitInsurance( + coverage: Reference(id: "coverage-id".asFHIRStringPrimitive()), + focal: FHIRPrimitive(true) + ) + ], + insurer: Reference(id: "insurer-id".asFHIRStringPrimitive()), + outcome: FHIRPrimitive(.complete), + patient: Reference(id: "patient-id".asFHIRStringPrimitive()), + provider: Reference(id: "provider-id".asFHIRStringPrimitive()), + status: FHIRPrimitive(.active), + type: CodeableConcept( + coding: [ + Coding(code: "type".asFHIRStringPrimitive()) + ] + ), + use: FHIRPrimitive(.claim) + ) + } + + static func createImmunization(date: Date) throws -> ModelsR4.Immunization { + ModelsR4.Immunization( + id: "immunization-id".asFHIRStringPrimitive(), + occurrence: .dateTime(FHIRPrimitive(try DateTime(date: date))), + patient: Reference(id: "patient-id".asFHIRStringPrimitive()), + status: FHIRPrimitive(.completed), + vaccineCode: CodeableConcept( + coding: [ + Coding(code: "vaccine-code".asFHIRStringPrimitive()) + ] + ) + ) + } + + static func createImmunizationEvaluation() -> ModelsR4.ImmunizationEvaluation { + ModelsR4.ImmunizationEvaluation( + doseStatus: CodeableConcept( + coding: [ + Coding(code: "dose-status".asFHIRStringPrimitive()) + ] + ), + id: "immunization-eval-id".asFHIRStringPrimitive(), + immunizationEvent: Reference(id: "event-id".asFHIRStringPrimitive()), + patient: Reference(id: "patient-id".asFHIRStringPrimitive()), + status: FHIRPrimitive(.completed), + targetDisease: CodeableConcept( + coding: [ + Coding(code: "targe-disease".asFHIRStringPrimitive()) + ] + ) + ) + } + + static func createImmunizationRecommendation(date: Date) throws -> ModelsR4.ImmunizationRecommendation { + ModelsR4.ImmunizationRecommendation( + date: FHIRPrimitive(try DateTime(date: date)), + id: "immunization-recommendation-id".asFHIRStringPrimitive(), + patient: Reference(id: "patient-id".asFHIRStringPrimitive()), + recommendation: [] + ) + } + + static func createMedication() -> ModelsR4.Medication { + ModelsR4.Medication(id: "medication-id".asFHIRStringPrimitive()) + } + + static func createMedicationDispense() -> ModelsR4.MedicationDispense { + ModelsR4.MedicationDispense( + id: "medication-dispense-id".asFHIRStringPrimitive(), + medication: .codeableConcept( + CodeableConcept(coding: [ + Coding(code: "med-code".asFHIRStringPrimitive()) + ]) + ), + status: FHIRPrimitive(.completed) + ) + } + + static func createMedicationKnowledge() -> ModelsR4.MedicationKnowledge { + ModelsR4.MedicationKnowledge(id: "medication-knowledge-id".asFHIRStringPrimitive()) + } + + static func createMedicationRequest(date: Date) throws -> ModelsR4.MedicationRequest { + ModelsR4.MedicationRequest( + authoredOn: FHIRPrimitive(try DateTime(date: date)), + id: "medication-request-id".asFHIRStringPrimitive(), + intent: FHIRPrimitive(.order), + medication: .codeableConcept( + CodeableConcept(coding: [ + Coding(code: "med-code".asFHIRStringPrimitive()) + ]) + ), + status: FHIRPrimitive(.active), + subject: Reference(id: "patient-id".asFHIRStringPrimitive()) + ) + } + + static func createMedicationAdministration(date: Date) throws -> ModelsR4.MedicationAdministration { + ModelsR4.MedicationAdministration( + effective: .dateTime(FHIRPrimitive(try DateTime(date: date))), + id: "medication-administration-id".asFHIRStringPrimitive(), + medication: .codeableConcept( + CodeableConcept(coding: [ + Coding(code: "med-code".asFHIRStringPrimitive()) + ]) + ), + status: FHIRPrimitive(.completed), + subject: Reference(id: "patient-id".asFHIRStringPrimitive()) + ) + } + + static func createMedicationStatement() -> ModelsR4.MedicationStatement { + ModelsR4.MedicationStatement( + id: "medication-statement-id".asFHIRStringPrimitive(), + medication: .codeableConcept( + CodeableConcept(coding: [ + Coding(code: "med-code".asFHIRStringPrimitive()) + ]) + ), + status: FHIRPrimitive(.completed), + subject: Reference(id: "patient-id".asFHIRStringPrimitive()) + ) + } + + static func createObservation(date: Date) throws -> ModelsR4.Observation { + ModelsR4.Observation( + code: CodeableConcept(coding: [Coding(code: "code".asFHIRStringPrimitive())]), + id: "observation-id".asFHIRStringPrimitive(), + issued: FHIRPrimitive(try Instant(date: date)), + status: FHIRPrimitive(.final) + ) + } + + static func createObservationDefinition() -> ModelsR4.ObservationDefinition { + ModelsR4.ObservationDefinition( + code: CodeableConcept(coding: [Coding(code: "code".asFHIRStringPrimitive())]), + id: "observation-definition-id".asFHIRStringPrimitive() + ) + } + + static func createProcedure(date: Date) throws -> ModelsR4.Procedure { + ModelsR4.Procedure( + id: "procedure-id".asFHIRStringPrimitive(), + performed: .dateTime(FHIRPrimitive(try DateTime(date: date))), + status: FHIRPrimitive(.completed), + subject: Reference(id: "patient-id".asFHIRStringPrimitive()) + ) + } + + static func createPatient(date: Date) throws -> ModelsR4.Patient { + guard let utcTimeZone = TimeZone(abbreviation: "UTC") else { + preconditionFailure("Failed to create UTC timezone") + } + + return ModelsR4.Patient( + birthDate: FHIRPrimitive(try FHIRDate(date: date, timeZone: utcTimeZone)), + id: "patient-id".asFHIRStringPrimitive() + ) + } + + static func createProvenance(date: Date) throws -> ModelsR4.Provenance { + ModelsR4.Provenance( + agent: [ + ProvenanceAgent( + type: CodeableConcept( + coding: [Coding(code: "agent-type".asFHIRStringPrimitive())] + ), + who: Reference(id: "agent-id".asFHIRStringPrimitive()) + ) + ], + id: "provenance-id".asFHIRStringPrimitive(), + recorded: FHIRPrimitive(try Instant(date: date)), + target: [ + Reference(id: "target-id".asFHIRStringPrimitive()) + ] + ) + } + + static func createSupplyDelivery(date: Date) throws -> ModelsR4.SupplyDelivery { + ModelsR4.SupplyDelivery( + id: "supply-delivery-id".asFHIRStringPrimitive(), + occurrence: .dateTime(FHIRPrimitive(try DateTime(date: date))), + status: FHIRPrimitive(.completed) + ) + } + } +} diff --git a/Tests/SpeziFHIRTests/FHIRResourceTests/FHIRResourceSearchTests.swift b/Tests/SpeziFHIRTests/FHIRResourceTests/FHIRResourceSearchTests.swift new file mode 100644 index 0000000..507d187 --- /dev/null +++ b/Tests/SpeziFHIRTests/FHIRResourceTests/FHIRResourceSearchTests.swift @@ -0,0 +1,57 @@ +// +// This source file is part of the Stanford Spezi open-source project +// +// SPDX-FileCopyrightText: 2025 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import ModelsR4 +@testable import SpeziFHIR +import XCTest + +extension FHIRResourceTests { + func testMatchesDisplayName() throws { + let observation = try ModelsR4Mocks.createObservation(date: testDate) + + let resource = FHIRResource( + versionedResource: .r4(observation), + displayName: "Test Resource" + ) + + XCTAssertTrue(resource.matchesDisplayName(with: "test")) + XCTAssertTrue(resource.matchesDisplayName(with: "resource")) + XCTAssertTrue(resource.matchesDisplayName(with: " test ")) + XCTAssertTrue(resource.matchesDisplayName(with: "TEST")) + XCTAssertFalse(resource.matchesDisplayName(with: "xyz")) + XCTAssertFalse(resource.matchesDisplayName(with: "")) + } + + func testFilterByDisplayName() throws { + let observation = try ModelsR4Mocks.createObservation(date: testDate) + let patient = try ModelsR4Mocks.createPatient(date: testDate) + let medicationRequest = try ModelsR4Mocks.createMedicationRequest(date: testDate) + + let resource1 = FHIRResource( + versionedResource: .r4(observation), + displayName: "Test Resource1" + ) + + let resource2 = FHIRResource( + versionedResource: .r4(patient), + displayName: "Test Resource2" + ) + + let resource3 = FHIRResource( + versionedResource: .r4(medicationRequest), + displayName: "Test Resource3" + ) + + let resources = [resource1, resource2, resource3] + + XCTAssertEqual(resources.filterByDisplayName(with: "test").count, 3) + XCTAssertEqual(resources.filterByDisplayName(with: "resource1").count, 1) + XCTAssertEqual(resources.filterByDisplayName(with: "xyz").count, 0) + XCTAssertEqual(resources.filterByDisplayName(with: "").count, 3) + } +} diff --git a/Tests/SpeziFHIRTests/FHIRResourceTests/FHIRResourceTests.swift b/Tests/SpeziFHIRTests/FHIRResourceTests/FHIRResourceTests.swift new file mode 100644 index 0000000..724a226 --- /dev/null +++ b/Tests/SpeziFHIRTests/FHIRResourceTests/FHIRResourceTests.swift @@ -0,0 +1,170 @@ +// +// This source file is part of the Stanford Spezi open-source project +// +// SPDX-FileCopyrightText: 2025 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import ModelsDSTU2 +import ModelsR4 +@testable import SpeziFHIR +import XCTest + + +class FHIRResourceTests: XCTestCase { + private static let utcDateFormatter: DateFormatter = { + let formatter = DateFormatter() + formatter.dateFormat = "yyyy-MM-dd" + formatter.timeZone = TimeZone(abbreviation: "UTC") + return formatter + }() + + let testDate: Date = { + guard let date = utcDateFormatter.date(from: "2025-01-01") else { + preconditionFailure("Failed to parse date: Invalid date string format") + } + return date + }() + + + func testModelsR4ResourceInitialization() throws { + let mockObservation = try ModelsR4Mocks.createObservation(date: testDate) + + let resource = FHIRResource( + resource: mockObservation, + displayName: "Test Observation" + ) + + XCTAssertEqual(resource.id, "observation-id") + XCTAssertEqual(resource.displayName, "Test Observation") + XCTAssertEqual(resource.resourceType, "Observation") + } + + func testModelsDSTU2ResourceInitialization() throws { + let mockObservation = try ModelsDSTU2Mocks.createObservation(date: testDate) + + let resource = FHIRResource( + resource: mockObservation, + displayName: "Test Observation" + ) + + XCTAssertEqual(resource.id, "observation-id") + XCTAssertEqual(resource.displayName, "Test Observation") + XCTAssertEqual(resource.resourceType, "Observation") + } + + func testModelsR4ResourceDates() throws { + let modelsR4Resources: [(ModelsR4.Resource, String)] = try [ + (ModelsR4Mocks.createCarePlan(date: testDate), "CarePlan"), + (ModelsR4Mocks.createCareTeam(date: testDate), "CareTeam"), + (ModelsR4Mocks.createClaim(date: testDate), "Claim"), + (ModelsR4Mocks.createCondition(date: testDate), "Condition"), + (ModelsR4Mocks.createDevice(date: testDate), "Device"), + (ModelsR4Mocks.createDiagnosticReport(date: testDate), "DiagnosticReport"), + (ModelsR4Mocks.createDocumentReference(date: testDate), "DocumentReference"), + (ModelsR4Mocks.createEncounter(date: testDate), "Encounter"), + (ModelsR4Mocks.createExplanationOfBenefit(date: testDate), "ExplanationOfBenefit"), + (ModelsR4Mocks.createImmunization(date: testDate), "Immunization"), + (ModelsR4Mocks.createMedicationRequest(date: testDate), "MedicationRequest"), + (ModelsR4Mocks.createMedicationAdministration(date: testDate), "MedicationAdministration"), + (ModelsR4Mocks.createObservation(date: testDate), "Observation"), + (ModelsR4Mocks.createProcedure(date: testDate), "Procedure"), + (ModelsR4Mocks.createPatient(date: testDate), "Patient"), + (ModelsR4Mocks.createProvenance(date: testDate), "Provenance"), + (ModelsR4Mocks.createSupplyDelivery(date: testDate), "SupplyDelivery") + ] + + for (resource, name) in modelsR4Resources { + let fhirResource = FHIRResource( + versionedResource: .r4(resource), + displayName: "Test \(name)" + ) + + XCTAssertEqual( + fhirResource.date, + testDate, + "Date extraction failed for \(name)" + ) + } + } + + func testModelsDSTU2ResourceDates() throws { + let modelsDSTU2Resources: [(ModelsDSTU2.Resource, String)] = try [ + (ModelsDSTU2Mocks.createObservation(date: testDate), "Observation"), + (ModelsDSTU2Mocks.createMedicationOrder(date: testDate), "MedicationOrder"), + (ModelsDSTU2Mocks.createMedicationStatement(date: testDate), "MedicationStatement"), + (ModelsDSTU2Mocks.createCondition(date: testDate), "Condition"), + (ModelsDSTU2Mocks.createProcedure(date: testDate), "Procedure"), + (ModelsDSTU2Mocks.createProcedure(date: testDate, usePeriod: false), "Procedure"), + (ModelsDSTU2Mocks.createProcedure(date: testDate, usePeriod: true), "Procedure with Period") + ] + + for (resource, name) in modelsDSTU2Resources { + let fhirResource = FHIRResource( + versionedResource: .dstu2(resource), + displayName: "Test \(name)" + ) + + XCTAssertEqual( + fhirResource.date, + testDate, + "Date extraction failed for DSTU2 \(name)" + ) + } + } + + func testModelsR4JSON() throws { + let observation = ModelsR4.Observation( + code: CodeableConcept( + coding: [ + Coding(code: "test-code".asFHIRStringPrimitive()) + ] + ), + id: "test-id".asFHIRStringPrimitive(), + status: FHIRPrimitive(.final) + ) + + let resource = FHIRResource( + versionedResource: .r4(observation), + displayName: "Test" + ) + + let jsonString = resource.json(withConfiguration: []) + + let jsonData = jsonString.data(using: .utf8) ?? Data() + let decoder = JSONDecoder() + let decodedObservation = try decoder.decode(ModelsR4.Observation.self, from: jsonData) + + XCTAssertEqual(decodedObservation.id, observation.id) + XCTAssertEqual(decodedObservation.code, observation.code) + XCTAssertEqual(decodedObservation.status, observation.status) + } + + func testModelsDSTU2JSON() throws { + let observation = ModelsDSTU2.Observation( + code: CodeableConcept( + coding: [ + Coding(code: "test-code".asFHIRStringPrimitive()) + ] + ), + id: "test-id".asFHIRStringPrimitive(), + status: FHIRPrimitive(.final) + ) + + let resource = FHIRResource( + versionedResource: .dstu2(observation), + displayName: "Test" + ) + + let jsonString = resource.json(withConfiguration: []) + + let jsonData = jsonString.data(using: .utf8) ?? Data() + let decoder = JSONDecoder() + let decodedObservation = try decoder.decode(ModelsDSTU2.Observation.self, from: jsonData) + + XCTAssertEqual(decodedObservation.id, observation.id) + XCTAssertEqual(decodedObservation.code, observation.code) + XCTAssertEqual(decodedObservation.status, observation.status) + } +} diff --git a/Tests/SpeziFHIRTests/FHIRResourceTests/ResourceProxyDisplayNameTests.swift b/Tests/SpeziFHIRTests/FHIRResourceTests/ResourceProxyDisplayNameTests.swift new file mode 100644 index 0000000..6f6b36a --- /dev/null +++ b/Tests/SpeziFHIRTests/FHIRResourceTests/ResourceProxyDisplayNameTests.swift @@ -0,0 +1,131 @@ +// +// This source file is part of the Stanford Spezi open-source project +// +// SPDX-FileCopyrightText: 2025 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import ModelsR4 +@testable import SpeziFHIR +import XCTest + + +extension FHIRResourceTests { + func testConditionDisplayName() throws { + let mockCondition = try ModelsR4Mocks.createCondition(date: testDate) + + // Test with text value + mockCondition.code = CodeableConcept(text: "Hypertension") + let proxy = ResourceProxy(with: mockCondition) + XCTAssertEqual(proxy.displayName, "Hypertension") + + // Test with no code + mockCondition.code = nil + XCTAssertEqual(proxy.displayName, "Condition") + } + + func testDiagnosticReportDisplayName() throws { + let mockReport = try ModelsR4Mocks.createDiagnosticReport(date: testDate) + + // Test with display coding + mockReport.code.coding = [Coding(display: "Blood Test")] + let proxy = ResourceProxy(with: mockReport) + XCTAssertEqual(proxy.displayName, "Blood Test") + + // Test with no codings + mockReport.code.coding?.removeAll() + XCTAssertEqual(proxy.displayName, "DiagnosticReport") + } + + func testEncounterDisplayName() throws { + let mockEncounter = try ModelsR4Mocks.createEncounter(date: testDate) + + // Test with reason code + mockEncounter.reasonCode = [CodeableConcept(coding: [Coding(display: "Follow-up")])] + let proxy = ResourceProxy(with: mockEncounter) + XCTAssertEqual(proxy.displayName, "Follow-up") + + // Test with encounter type + mockEncounter.reasonCode = nil + mockEncounter.type = [CodeableConcept(coding: [Coding(display: "Office Visit")])] + XCTAssertEqual(proxy.displayName, "Office Visit") + + // Test with no type or reason + mockEncounter.type = nil + XCTAssertEqual(proxy.displayName, "Encounter") + } + + func testImmunizationDisplayName() throws { + let mockImmunization = try ModelsR4Mocks.createImmunization(date: testDate) + + // Test with vaccine text + mockImmunization.vaccineCode.text = "Flu Shot" + let proxy = ResourceProxy(with: mockImmunization) + XCTAssertEqual(proxy.displayName, "Flu Shot") + + // Test with no vaccine text + mockImmunization.vaccineCode.text = nil + XCTAssertEqual(proxy.displayName, "Immunization") + } + + func testMedicationRequestDisplayName() throws { + let mockMedRequest = try ModelsR4Mocks.createMedicationRequest(date: testDate) + + // Test with codeable concept text + if case let .codeableConcept(medicationCode) = mockMedRequest.medication { + medicationCode.text = "Aspirin" + } + let proxy = ResourceProxy(with: mockMedRequest) + XCTAssertEqual(proxy.displayName, "Aspirin") + + // Test with no text in codeable concept + if case let .codeableConcept(medicationCode) = mockMedRequest.medication { + medicationCode.text = nil + } + XCTAssertEqual(proxy.displayName, "MedicationRequest") + + // Test with reference instead of codeable concept + mockMedRequest.medication = .reference(Reference()) + XCTAssertEqual(proxy.displayName, "MedicationRequest") + } + + func testObservationDisplayName() throws { + let mockObservation = try ModelsR4Mocks.createObservation(date: testDate) + + // Test with code text + mockObservation.code.text = "Blood Pressure" + let proxy = ResourceProxy(with: mockObservation) + XCTAssertEqual(proxy.displayName, "Blood Pressure") + + // Test with no code text + mockObservation.code.text = nil + XCTAssertEqual(proxy.displayName, "Observation") + } + + func testProcedureDisplayName() throws { + let mockProcedure = try ModelsR4Mocks.createProcedure(date: testDate) + + // Test with code text + mockProcedure.code = CodeableConcept(text: "Hip Surgery") + let proxy = ResourceProxy(with: mockProcedure) + XCTAssertEqual(proxy.displayName, "Hip Surgery") + + // Test with no code + mockProcedure.code = nil + XCTAssertEqual(proxy.displayName, "Procedure") + } + + func testPatientDisplayName() throws { + let mockPatient = try ModelsR4Mocks.createPatient(date: testDate) + + // Test with name components + mockPatient.name = [HumanName(family: "Doe", given: ["John"])] + let proxy = ResourceProxy(with: mockPatient) + XCTAssertEqual(proxy.displayName, "JohnDoe") + + // Test with no name + mockPatient.name = nil + XCTAssertEqual(proxy.displayName, "Patient") + } +}