From f8d142b32b6887b0faf2121c583fc04b4fc8c541 Mon Sep 17 00:00:00 2001 From: Kenneth Myhra Date: Thu, 9 Apr 2026 22:41:12 +0200 Subject: [PATCH 1/5] Guard against nil clients Guard against nil client in TestScriptEngine#initialize and BaseTest#version_namespace when calling fhir_version, which occurs when TestScriptEngine is instantiated without a client (e.g. list_all). --- lib/tests/base_test.rb | 4 ++-- lib/tests/testscripts/testscript_engine.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/tests/base_test.rb b/lib/tests/base_test.rb index 7d2ab30..2954a5c 100644 --- a/lib/tests/base_test.rb +++ b/lib/tests/base_test.rb @@ -50,9 +50,9 @@ def initialize(client, client2=nil) end def version_namespace - if @client.fhir_version.to_s.upcase == 'DSTU2' + if @client&.fhir_version.to_s.upcase == 'DSTU2' "FHIR::DSTU2".constantize - elsif @client.fhir_version.to_s.upcase == 'STU3' + elsif @client&.fhir_version.to_s.upcase == 'STU3' "FHIR::STU3".constantize else "FHIR".constantize diff --git a/lib/tests/testscripts/testscript_engine.rb b/lib/tests/testscripts/testscript_engine.rb index c49d5c6..85a11af 100644 --- a/lib/tests/testscripts/testscript_engine.rb +++ b/lib/tests/testscripts/testscript_engine.rb @@ -8,7 +8,7 @@ def initialize(client=nil, client2=nil) @client = client @client2 = client2 @scripts = [] - load_testscripts if client.fhir_version != :dstu2 # Run tests scripts on STU3+ only. + load_testscripts if client&.fhir_version != :dstu2 # Run tests scripts on STU3+ only. end def tests From 52d7ab9f3f05c1ecc1346a6b3efc3e1b734ff640 Mon Sep 17 00:00:00 2001 From: Kenneth Myhra Date: Thu, 9 Apr 2026 22:42:16 +0200 Subject: [PATCH 2/5] Fix TypeError in ResourceGenerator DSTU2 Quantity exclusion ignore_multiple_types is an Array, so concatenating a String raises TypeError: no implicit conversion of String into Array. Use an Array literal instead. --- lib/resource_generator.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/resource_generator.rb b/lib/resource_generator.rb index 0fa2eb6..7f5d33f 100644 --- a/lib/resource_generator.rb +++ b/lib/resource_generator.rb @@ -42,7 +42,7 @@ def self.set_fields!(resource, namespace, embedded=0) # definition in the DSTU2 models. So here, we're just skipping Quantity choice, # and selecting some other (probably primitive) type for the multi-choice FHIR property. ignore_multiple_types = ['Meta'] - ignore_multiple_types += 'Quantity' if namespace == 'FHIR::DSTU2' + ignore_multiple_types += ['Quantity'] if namespace == 'FHIR::DSTU2' selected_multiples = multiples.map { |k| "#{k}#{resource.class::MULTIPLE_TYPES[k].reject { |t| ignore_multiple_types.include?(t) }.sample.titleize.split.join}" } unselected_multiples = all_multiples - selected_multiples end From 5e6c008ad56372c360e0005fdd7df0cebc0d78dd Mon Sep 17 00:00:00 2001 From: Kenneth Myhra Date: Thu, 9 Apr 2026 22:42:37 +0200 Subject: [PATCH 3/5] Skip CodeableConcept optional fields with required external binding When an optional (min=0) CodeableConcept field has a required binding to an external ValueSet (e.g. LOINC, SNOMED) and no valid_codes are available in the model metadata, the generator produced a random code that always fails validation. Since these fields are optional (min=0), skip them to generate valid resources. --- lib/resource_generator.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/resource_generator.rb b/lib/resource_generator.rb index 7f5d33f..9e702e6 100644 --- a/lib/resource_generator.rb +++ b/lib/resource_generator.rb @@ -121,6 +121,8 @@ def self.set_fields!(resource, namespace, embedded=0) c.system = 'https://www.usps.com/' c.code = ['CA','TX','NY','MA','DC'].sample end + elsif type == 'CodeableConcept' && meta['binding'] && meta['binding']['strength'] == 'required' && !meta['valid_codes'] && meta['min'] == 0 + gen = nil # Cannot generate valid code for external required binding (e.g. LOINC/SNOMED); field is optional so safe to skip elsif type == 'Coding' && meta['valid_codes'] && meta['binding'] gen.system = meta['valid_codes'].keys.sample gen.code = meta['valid_codes'][gen.system].sample From 6803e1d2556497e370d3294c8b40baf0270447cb Mon Sep 17 00:00:00 2001 From: Kenneth Myhra Date: Fri, 10 Apr 2026 09:44:59 +0200 Subject: [PATCH 4/5] Use dynamic key instead of hardcoded script name in test_testscript_find The spec example testscripts are intentionally not loaded in CI/production, so hardcoding 'TS-testscript-example' always fails. Use tests.keys.first to reference whatever testscript is actually loaded. --- test/unit/metadata_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/metadata_test.rb b/test/unit/metadata_test.rb index d01af79..c58db8d 100644 --- a/test/unit/metadata_test.rb +++ b/test/unit/metadata_test.rb @@ -36,7 +36,7 @@ def test_testscript_find testscript_engine = Crucible::Tests::TestScriptEngine.new(nil) - keyed_test = testscript_engine.find_test('TS-testscript-example') + keyed_test = testscript_engine.find_test(tests.keys.first) assert !keyed_test.nil?, "Failed to find testscript by key" end From 1603ea24a016b881ee32930cafd1af89412aa6d9 Mon Sep 17 00:00:00 2001 From: Kenneth Myhra Date: Thu, 9 Apr 2026 23:03:33 +0200 Subject: [PATCH 5/5] Add GitHub Actions workflow for unit tests Runs bundle exec rake test_unit on every push to master and on every pull request, without requiring a FHIR server. --- .github/workflows/unit-tests.yml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 .github/workflows/unit-tests.yml diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml new file mode 100644 index 0000000..04f8d6c --- /dev/null +++ b/.github/workflows/unit-tests.yml @@ -0,0 +1,24 @@ +name: Unit Tests + +on: + push: + branches: + - master + pull_request: + +jobs: + test: + runs-on: ubuntu-24.04 + + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.0' + bundler-cache: true + + - name: Run unit tests + run: bundle exec rake test_unit