From 834aed9f86bfa933db475fb28d787b8107fdf9e4 Mon Sep 17 00:00:00 2001 From: Marco Visser Date: Tue, 18 Jan 2022 14:42:25 +0100 Subject: [PATCH] After review Marten: rewrote the unittests to the new vValueSetValidateCode method --- .../Source/TerminologyTests.cs | 166 +++++++++++++++++- .../Properties/AssemblyInfo.cs | 5 +- 2 files changed, 161 insertions(+), 10 deletions(-) diff --git a/src/Hl7.Fhir.Specification.Tests/Source/TerminologyTests.cs b/src/Hl7.Fhir.Specification.Tests/Source/TerminologyTests.cs index 8aa39a1550..09179b2a46 100644 --- a/src/Hl7.Fhir.Specification.Tests/Source/TerminologyTests.cs +++ b/src/Hl7.Fhir.Specification.Tests/Source/TerminologyTests.cs @@ -1,3 +1,4 @@ +using FluentAssertions; using Hl7.Fhir.Model; using Hl7.Fhir.Rest; using Hl7.Fhir.Specification.Source; @@ -160,6 +161,71 @@ public async T.Task TestPropertyRetrieval() Assert.True(conceptQuestion.ListConceptProperties(testCs, CodeSystem.CONCEPTPROPERTY_NOT_SELECTABLE).Any()); } + + private async T.Task testServiceAsync(ITerminologyService svc) + { + var vsUrl = "http://hl7.org/fhir/ValueSet/data-absent-reason"; + var result = await validateCodedValue(svc, vsUrl, code: "NaN", system: "http://hl7.org/fhir/data-absent-reason"); + isSuccess(result).Should().BeTrue(); + + result = await validateCodedValue(svc, vsUrl, code: "NaNX", system: "http://hl7.org/fhir/data-absent-reason"); + isSuccess(result).Should().BeFalse(); + + result = await validateCodedValue(svc, vsUrl, code: "NaN", system: "http://hl7.org/fhir/data-absent-reason", + display: "Not a Number"); + isSuccess(result).Should().BeTrue(); + + // The spec is not clear on the behaviour of incorrect displays - so don't test it here + //result = svc.ValidateCode(vsUrl, code: "NaN", system: "http://hl7.org/fhir/data-absent-reason", + // display: "Not any Number"); + //Assert.True(result.Success); + + result = await validateCodedValue(svc, "http://hl7.org/fhir/ValueSet/v3-AcknowledgementDetailCode", code: "_AcknowledgementDetailNotSupportedCode", + system: "http://hl7.org/fhir/v3/AcknowledgementDetailCode"); + isSuccess(result).Should().BeTrue(); + + await Assert.ThrowsAsync(async () => await validateCodedValue(svc, "http://hl7.org/fhir/ValueSet/crappy", code: "4322002", system: "http://snomed.info/sct")); + + var coding = new Coding("http://hl7.org/fhir/data-absent-reason", "NaN"); + result = await validateCodedValue(svc, vsUrl, coding: coding); + isSuccess(result).Should().BeTrue(); + + coding.Display = "Not a Number"; + result = await validateCodedValue(svc, vsUrl, coding: coding); + isSuccess(result).Should().BeTrue(); + + coding.Code = "NaNX"; + result = await validateCodedValue(svc, vsUrl, coding: coding); + isSuccess(result).Should().BeFalse(); + coding.Code = "NaN"; + + var cc = new CodeableConcept("http://hl7.org/fhir/data-absent-reason", "NaNX", "Not a Number"); + result = await validateCodedValue(svc, vsUrl, codeableConcept: cc); + isSuccess(result).Should().BeFalse(); + + cc.Coding.Add(new Coding("http://hl7.org/fhir/data-absent-reason", "asked")); + result = await validateCodedValue(svc, vsUrl, codeableConcept: cc); + + isSuccess(result).Should().BeTrue(); + } + + [Fact] + public async T.Task LocalTSDisplayIncorrectAsWarningAsync() + { + var svc = new LocalTerminologyService(_resolver); + + var vsUrl = "http://hl7.org/fhir/ValueSet/data-absent-reason"; + var result = await validateCodedValue(svc, vsUrl, code: "NaN", system: "http://hl7.org/fhir/data-absent-reason", + display: "Not a Number"); + isSuccess(result).Should().BeTrue(); + hasWarnings(result).Should().BeFalse(); + + result = await validateCodedValue(svc, vsUrl, code: "NaN", system: "http://hl7.org/fhir/data-absent-reason", + display: "Certainly Not a Number"); + isSuccess(result).Should().BeTrue(); + hasWarnings(result).Should().BeTrue(); + } + [Fact] public async void LocalTSDisplayIncorrectAsMessage() { @@ -170,8 +236,8 @@ public async void LocalTSDisplayIncorrectAsMessage() var result = await svc.ValueSetValidateCode(inParams); - Assert.True(result.GetSingleValue("result")?.Value); - Assert.Null(result.GetSingleValue("message")); + isSuccess(result).Should().BeTrue(); + hasWarnings(result).Should().BeFalse(); inParams = new ValidateCodeParameters() .WithValueSet(url: "http://hl7.org/fhir/ValueSet/data-absent-reason") @@ -179,10 +245,31 @@ public async void LocalTSDisplayIncorrectAsMessage() result = await svc.ValueSetValidateCode(inParams); - Assert.True(result.GetSingleValue("result")?.Value); - Assert.NotNull(result.GetSingleValue("message")); + isSuccess(result).Should().BeTrue(); + hasWarnings(result).Should().BeTrue(); } + [Fact] + public async T.Task LocalTermServiceValidateCodeTest() + { + var svc = new LocalTerminologyService(_resolver); + + // Do common tests for service + await testServiceAsync(svc); + + // This is a valueset with a compose - not supported locally normally, but it has been expanded in the zip, so this will work + var result = await validateCodedValue(svc, url: "http://hl7.org/fhir/ValueSet/yesnodontknow", code: "Y", system: "http://hl7.org/fhir/v2/0136"); + isSuccess(result).Should().BeTrue(); + + // This test is not always correctly done by the external services, so copied here instead + result = await validateCodedValue(svc, url: "http://hl7.org/fhir/ValueSet/v3-AcknowledgementDetailCode", code: "_AcknowledgementDetailNotSupportedCode", system: "http://hl7.org/fhir/v3/AcknowledgementDetailCode"); + isSuccess(result).Should().BeTrue(); + + // And one that will specifically fail on the local service, since it's too complex too expand - the local term server won't help you here + await Assert.ThrowsAsync(async () => await validateCodedValue(svc, url: "http://hl7.org/fhir/ValueSet/substance-code", code: "1166006", system: "http://snomed.info/sct")); + } + + [Fact] public async void LocalTermServiceValidateCodeWithParamsTest() { @@ -194,7 +281,7 @@ public async void LocalTermServiceValidateCodeWithParamsTest() .WithCode(code: "Y", system: "http://hl7.org/fhir/v2/0136"); var result = await svc.ValueSetValidateCode(inParams); - Assert.True(result.GetSingleValue("result")?.Value); + isSuccess(result).Should().BeTrue(); // This test is not always correctly done by the external services, so copied here instead inParams = new ValidateCodeParameters() @@ -204,7 +291,7 @@ public async void LocalTermServiceValidateCodeWithParamsTest() result = await svc.ValueSetValidateCode(inParams); - Assert.False(result.GetSingleValue("result")?.Value); + isSuccess(result).Should().BeFalse(); // And one that will specifically fail on the local service, since it's too complex too expand - the local term server won't help you here inParams = new ValidateCodeParameters() @@ -264,6 +351,17 @@ public async T.Task LocalTermServiceUsingDuplicateParameters() await Assert.ThrowsAsync(async () => await svc.ValueSetValidateCode(inParams)); } + [Fact] + public async T.Task TestOperationOutcomesAsync() + { + var svc = new LocalTerminologyService(_resolver); + + var result = await validateCodedValue(svc, "http://hl7.org/fhir/ValueSet/administrative-gender", code: "test", context: "Partient.gender"); + + isSuccess(result).Should().BeFalse(); + getMessage(result).Should().Contain("does not exist in valueset"); + } + [Fact(), Trait("TestCategory", "IntegrationTest")] public async void ExternalServiceTranslateSimpleTranslate() @@ -760,6 +858,21 @@ public async void ExternalServiceValidateCodeTest() Assert.True(result.Value); } + [Fact(Skip = "Don't want to run these kind of integration tests anymore"), Trait("TestCategory", "IntegrationTest")] + public async T.Task FallbackServiceValidateCodeTestAsync() + { + var client = new FhirClient(_externalTerminologyServerEndpoint); + var external = new ExternalTerminologyService(client); + var local = new LocalTerminologyService(_resolver); + var svc = new FallbackTerminologyService(local, external); + + await testServiceAsync(svc); + + // Now, this should fall back + var result = await validateCodedValue(svc, "http://hl7.org/fhir/ValueSet/substance-code", code: "1166006", system: "http://snomed.info/sct"); + isSuccess(result).Should().BeTrue(); + } + [Fact(Skip = "Don't want to run these kind of integration tests anymore"), Trait("TestCategory", "IntegrationTest")] public async void FallbackServiceValidateCodeWithParamsTest() { @@ -777,6 +890,47 @@ public async void FallbackServiceValidateCodeWithParamsTest() Assert.True(result.GetSingleValue("result")?.Value); } + [Fact(Skip = "Don't want to run these kind of integration tests anymore"), Trait("TestCategory", "IntegrationTest")] + public async T.Task FallbackServiceValidateCodeTestWithVS() + { + var client = new FhirClient(_externalTerminologyServerEndpoint); + var service = new ExternalTerminologyService(client); + var vs = await _resolver.FindValueSetAsync("http://hl7.org/fhir/ValueSet/substance-code"); + Assert.NotNull(vs); + + // Override the canonical with something the remote server cannot know + vs.Url = "http://furore.com/fhir/ValueSet/testVS"; + var local = new LocalTerminologyService(new IKnowOnlyMyTestVSResolver(vs)); + var fallback = new FallbackTerminologyService(local, service); + + // Now, this should fall back to external + send our vs (that the server cannot know about) + var result = await validateCodedValue(fallback, "http://furore.com/fhir/ValueSet/testVS", code: "1166006", system: "http://snomed.info/sct"); + isSuccess(result).Should().BeTrue(); + } + + #region helper functions + private async T.Task validateCodedValue(ITerminologyService service, string url = null, string context = null, string code = null, + string system = null, string version = null, string display = null, + Coding coding = null, CodeableConcept codeableConcept = null) + { + var inParams = new ValidateCodeParameters() + .WithValueSet(url: url, context: context) + .WithCode(code: code, system: system, display: display) + .WithCoding(coding: coding) + .WithCodeableConcept(codeableConcept: codeableConcept); + return await service.ValueSetValidateCode(inParams); + } + + private static bool isSuccess(Parameters outcome) => outcome.GetSingleValue("result")?.Value ?? false; + + private static bool hasWarnings(Parameters outcome) => + isSuccess(outcome) && outcome.GetSingleValue("message") is not null; + + private static string? getMessage(Parameters outcome) => + outcome.GetSingleValue("message")?.Value; + + #endregion + private class IKnowOnlyMyTestVSResolver : IAsyncResourceResolver { public ValueSet _myOnlyVS; diff --git a/src/Hl7.Fhir.Specification/Properties/AssemblyInfo.cs b/src/Hl7.Fhir.Specification/Properties/AssemblyInfo.cs index 943654f6f8..8ef10e82b5 100644 --- a/src/Hl7.Fhir.Specification/Properties/AssemblyInfo.cs +++ b/src/Hl7.Fhir.Specification/Properties/AssemblyInfo.cs @@ -20,7 +20,4 @@ [assembly: TypeForwardedTo(typeof(IArtifactSource))] //Moved ITerminologyService to Hl7.Fhir.Support.Poco -[assembly: TypeForwardedTo(typeof(ITerminologyService))] - -//Moved TerminologyServiceExtensions to Hl7.Fhir.Support.Poco -[assembly: TypeForwardedTo(typeof(TerminologyServiceExtensions))] \ No newline at end of file +[assembly: TypeForwardedTo(typeof(ITerminologyService))] \ No newline at end of file