Repository: FHIR/sushi
Pull request #1428 (2024-02-19 13:03:13)

Assign FSH Code or Reference to CodeableReference element
Completes task [CIMPL-1220](https://standardhealthrecord.atlassian.net/browse/CIMPL-1220).

When writing an assignment rule, if the path refers to a CodeableReference element, the value may be a FshCode or FshReference. The value will be applied to the element's concept or reference child element, respectively. It is possible to have two rules with the same CodeableReference path if one assigns a FshCode and the other assigns a FshReference.

I ran a full regression with these changes, and there were no differences in any of the outputs.


Patch:
diff --git a/src/errors/AssignmentToCodeableReferenceError.ts b/src/errors/AssignmentToCodeableReferenceError.ts
deleted file mode 100644
index b91c4d8..0000000
--- a/src/errors/AssignmentToCodeableReferenceError.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { Annotated } from './Annotated';
-import { AssignmentValueType } from '../fshtypes/rules';
-
-export class AssignmentToCodeableReferenceError extends Error implements Annotated {
-  specReferences = ['https://build.fhir.org/references.html#CodeableReference'];
-  constructor(
-    public valueType: string,
-    public value: AssignmentValueType,
-    public childPath: string
-  ) {
-    super(
-      `Cannot assign ${valueType} value: ${value.toString()} to CodeableReference. Assign to CodeableReference.${childPath} instead.`
-    );
-  }
-}
diff --git a/src/errors/index.ts b/src/errors/index.ts
index 0fa4906..f61dab8 100644
--- a/src/errors/index.ts
+++ b/src/errors/index.ts
@@ -44,7 +44,6 @@ export * from './InvalidResourceParentError';
 export * from './InvalidTypeAccessError';
 export * from './NonAbstractParentOfSpecializationError';
 export * from './ValueConflictsWithClosedSlicingError';
-export * from './AssignmentToCodeableReferenceError';
 export * from './InvalidMustSupportError';
 export * from './InvalidChoiceTypeRulePathError';
 export * from './MismatchedBindingTypeError';
diff --git a/src/export/InstanceExporter.ts b/src/export/InstanceExporter.ts
index 37b4aee..6bdcfd0 100644
--- a/src/export/InstanceExporter.ts
+++ b/src/export/InstanceExporter.ts
@@ -188,15 +188,19 @@ export class InstanceExporter implements Fishable {
         // Record each valid rule in a map
         // Choice elements on an instance must use a specific type, so if the path still has an unchosen choice element,
         // the rule can't be used. See http://hl7.org/fhir/R4/formats.html#choice
+        let finalRulePath = rule.path;
+        if (validatedRule.childPath != null) {
+          finalRulePath = `${rule.path}.${validatedRule.childPath}`;
+        }
         if (validatedRule.pathParts.some(part => part.base.endsWith('[x]'))) {
           logger.error(
             `Unable to assign value at ${rule.path}: choice elements on an instance must use a specific type`,
             rule.sourceInfo
           );
-        } else if (!(validatedRule.assignedValue == null && ruleMap.has(rule.path))) {
+        } else if (!(validatedRule.assignedValue == null && ruleMap.has(finalRulePath))) {
           // If a validateRule doesn't have an assignedValue, it was a PathRule that has
           // no implied required values, so we don't need to set anything for this rule
-          ruleMap.set(rule.path, {
+          ruleMap.set(finalRulePath, {
             pathParts: validatedRule.pathParts,
             assignedValue: validatedRule.assignedValue,
             sourceInfo: rule.sourceInfo
diff --git a/src/fhirtypes/ElementDefinition.ts b/src/fhirtypes/ElementDefinition.ts
index 0617dc5..28a0330 100644
--- a/src/fhirtypes/ElementDefinition.ts
+++ b/src/fhirtypes/ElementDefinition.ts
@@ -12,7 +12,15 @@ import {
 import { minify } from 'html-minifier-terser';
 import { isUri } from 'valid-url';
 import { StructureDefinition } from './StructureDefinition';
-import { CodeableConcept, Coding, Element, Quantity, Ratio, Reference } from './dataTypes';
+import {
+  CodeableConcept,
+  CodeableReference,
+  Coding,
+  Element,
+  Quantity,
+  Ratio,
+  Reference
+} from './dataTypes';
 import {
   FshCanonical,
   FshCode,
@@ -24,7 +32,6 @@ import {
 } from '../fshtypes';
 import { AddElementRule, AssignmentValueType, OnlyRule, OnlyRuleType } from '../fshtypes/rules';
 import {
-  AssignmentToCodeableReferenceError,
   BindingStrengthError,
   CodedTypeNotFoundError,
   DuplicateSliceError,
@@ -258,6 +265,8 @@ export class ElementDefinition {
   patternRatio: Ratio;
   fixedReference: Reference;
   patternReference: Reference;
+  fixedCodeableReference: CodeableReference;
+  patternCodeableReference: CodeableReference;
   example: ElementDefinitionExample[];
   // minValue[x] can be many different field names (e.g., minValueDate, minValueQuantity, etc.),
   // so we can't easily use a getter/setter.  It will be just an unspecified property.  For now.
@@ -1792,7 +1801,7 @@ export class ElementDefinition {
       throw new NoSingleTypeError(type);
     }
 
-    // If assigning by pattern, ensure that it's not already assigned by fixed[x], because We can't overrided
+    // If assigning by pattern, ensure that it's not already assigned by fixed[x], because We can't override
     // fixed[x] with pattern[x] since pattern[x] is looser
     if (!exactly) {
       const fixedField = Object.entries(this).find(e => e[0].startsWith('fixed') && e[1] != null);
@@ -1840,12 +1849,6 @@ export class ElementDefinition {
       case 'Reference':
         value = value as FshReference;
 
-        // If we are assigning to a CodeableReference, we want to give a more descriptive error
-        // so we check the type and log this error separately from all other type checking
-        if (this.type[0].code === 'CodeableReference') {
-          throw new AssignmentToCodeableReferenceError('reference', value, 'reference');
-        }
-
         // It is possible the reference constraints do not come from the current element itself, so find the
         // element which is constraining the current element, and check the assignment against it
         const referenceConstrainingElement =
@@ -1865,7 +1868,22 @@ export class ElementDefinition {
             referenceConstrainingElement.type
           );
         }
-        this.assignFHIRValue(value.toString(), value.toFHIRReference(), exactly, 'Reference');
+        if (this.type[0].code === 'CodeableReference') {
+          // If we are assigning to a CodeableReference, we assign a value of a complex object containing the reference.
+          // If there's already a coding on the element, we want to keep it.
+          const existing =
+            this.fixedCodeableReference ??
+            this.patternCodeableReference ??
+            this.assignedByAnyParent();
+          const codeableReference: CodeableReference = { reference: value.toFHIRReference() };
+          if (existing?.concept != null) {
+            codeableReference.concept = existing.concept;
+          }
+          this.assignFHIRValue(value.toString(), codeableReference, exactly, 'CodeableReference');
+        } else {
+          this.assignFHIRValue(value.toString(), value.toFHIRReference(), exactly, 'Reference');
+        }
+
         break;
       case 'Canonical':
         value = value as FshCanonical;
@@ -2370,7 +2388,14 @@ export class ElementDefinition {
       }
       this.assignFHIRValue(code.toString(), quantity, exactly, type);
     } else if (type === 'CodeableReference') {
-      throw new AssignmentToCodeableReferenceError('code', code, 'concept');
+      // Similar to Quantity, we want to ensure that if a reference already exists, we keep it.
+      const existing =
+        this.fixedCodeableReference ?? this.patternCodeableReference ?? this.assignedByAnyParent();
+      const codeableReference: CodeableReference = { concept: code.toFHIRCodeableConcept() };
+      if (existing?.reference != null) {
+        codeableReference.reference = existing.reference;
+      }
+      this.assignFHIRValue(code.toString(), codeableReference, exactly, 'CodeableReference');
     } else {
       throw new MismatchedTypeError('code', code, type);
     }
diff --git a/src/fhirtypes/StructureDefinition.ts b/src/fhirtypes/StructureDefinition.ts
index b3ae8e0..10b1eed 100644
--- a/src/fhirtypes/StructureDefinition.ts
+++ b/src/fhirtypes/StructureDefinition.ts
@@ -30,7 +30,7 @@ import { parseFSHPath, assembleFSHPath } from '../utils/PathUtils';
 import { InstanceDefinition } from './InstanceDefinition';
 import { isUri } from 'valid-url';
 import { logger } from '../utils';
-import { SourceInfo } from '../fshtypes';
+import { FshCode, FshReference, SourceInfo } from '../fshtypes';
 
 /**
  * A class representing a FHIR R4 StructureDefinition.  For the most part, each allowable property in a StructureDefinition
@@ -509,7 +509,7 @@ export class StructureDefinition {
     inlineResourceTypes: string[] = [],
     sourceInfo: SourceInfo = null,
     manualSliceOrdering = false
-  ): { assignedValue: any; pathParts: PathPart[] } {
+  ): { assignedValue: any; pathParts: PathPart[]; childPath?: string } {
     const pathParts = parseFSHPath(path);
     let currentPath = '';
     let previousPath = '';
@@ -700,6 +700,15 @@ export class StructureDefinition {
         // @ts-ignore
         currentElement[originalKey] = originalValue;
       }
+      // when key ends with CodeableReference, and the original value is a FshReference or FshCode (and not the entire CodeableReference object)
+      // we add an extra field to the return value that indicates that we are doing CodeableReference tricks
+      if (key?.endsWith('CodeableReference')) {
+        if (value instanceof FshReference) {
+          return { assignedValue, pathParts, childPath: 'reference' };
+        } else if (value instanceof FshCode) {
+          return { assignedValue, pathParts, childPath: 'concept' };
+        }
+      }
     }
 
     return { assignedValue, pathParts };
diff --git a/src/fhirtypes/dataTypes.ts b/src/fhirtypes/dataTypes.ts
index 8bc66ff..2f3ede4 100644
--- a/src/fhirtypes/dataTypes.ts
+++ b/src/fhirtypes/dataTypes.ts
@@ -228,3 +228,13 @@ export type DomainResource = Resource & {
   extension?: Extension[];
   modifierExtension?: Extension[];
 };
+
+/**
+ * Represents the FHIR R5 data type CodeableReference
+ *
+ * @see {@link https://hl7.org/fhir/R5/references.html#CodeableReference}
+ */
+export type CodeableReference = {
+  reference?: Reference;
+  concept?: CodeableConcept;
+};


Test patch:
diff --git a/test/export/InstanceExporter.test.ts b/test/export/InstanceExporter.test.ts
index 42ab77f..04f23e8 100644
--- a/test/export/InstanceExporter.test.ts
+++ b/test/export/InstanceExporter.test.ts
@@ -11387,7 +11387,7 @@ describe('InstanceExporter R5', () => {
     });
 
     // Assignment on CodeableReference
-    it('should log a meaningful error when assigning a Reference directly to a CodeableReference', () => {
+    it('should set the reference child element when assigning a Reference directly to a CodeableReference', () => {
       const condition = new Instance('TestCondition');
       condition.instanceOf = 'Condition';
       const assignedIdRule = new AssignmentRule('id');
@@ -11403,13 +11403,14 @@ describe('InstanceExporter R5', () => {
       doc.instances.set(condition.name, condition);
       doc.instances.set(carePlan.name, carePlan);
       const exported = exportInstance(carePlan);
-      expect(exported.addresses).toBeUndefined();
-      expect(loggerSpy.getMessageAtIndex(0, 'error')).toMatch(
-        /Cannot assign.*Reference\(Condition\/condition-id\).*Assign to CodeableReference.reference\D*/s
-      );
+      expect(exported.addresses[0]).toEqual({
+        reference: {
+          reference: 'Condition/condition-id'
+        }
+      });
     });
 
-    it('should log a meaningful error when assigning a code directly to a CodeableReference', () => {
+    it('should set the concept child element when assigning a code directly to a CodeableReference', () => {
       const carePlan = new Instance('TestCarePlan');
       carePlan.instanceOf = 'CarePlan';
       const assignedRefRule = new AssignmentRule('addresses');
@@ -11418,10 +11419,63 @@ describe('InstanceExporter R5', () => {
 
       doc.instances.set(carePlan.name, carePlan);
       const exported = exportInstance(carePlan);
-      expect(exported.addresses).toBeUndefined();
-      expect(loggerSpy.getMessageAtIndex(0, 'error')).toMatch(
-        /Cannot assign.*#foo.*Assign to CodeableReference.concept\D*/s
-      );
+      expect(exported.addresses[0]).toEqual({
+        concept: {
+          coding: [{ code: 'foo' }]
+        }
+      });
+    });
+
+    it('should set both reference and concept when assigning directly to a CodeableReference', () => {
+      const condition = new Instance('TestCondition');
+      condition.instanceOf = 'Condition';
+      const assignedIdRule = new AssignmentRule('id');
+      assignedIdRule.value = 'condition-id';
+      condition.rules.push(assignedIdRule);
+
+      const carePlan = new Instance('TestCarePlan');
+      carePlan.instanceOf = 'CarePlan';
+      const assignedRefRule = new AssignmentRule('addresses');
+      assignedRefRule.value = new FshReference('TestCondition');
+      const assignedConceptRule = new AssignmentRule('addresses');
+      assignedConceptRule.value = new FshCode('foo');
+      carePlan.rules.push(assignedRefRule, assignedConceptRule);
+
+      doc.instances.set(condition.name, condition);
+      doc.instances.set(carePlan.name, carePlan);
+      const exported = exportInstance(carePlan);
+      expect(exported.addresses[0]).toEqual({
+        reference: { reference: 'Condition/condition-id' },
+        concept: {
+          coding: [{ code: 'foo' }]
+        }
+      });
+    });
+
+    it('should set both concept and reference when assigning directly to a CodeableReference', () => {
+      const condition = new Instance('TestCondition');
+      condition.instanceOf = 'Condition';
+      const assignedIdRule = new AssignmentRule('id');
+      assignedIdRule.value = 'condition-id';
+      condition.rules.push(assignedIdRule);
+
+      const carePlan = new Instance('TestCarePlan');
+      carePlan.instanceOf = 'CarePlan';
+      const assignedConceptRule = new AssignmentRule('addresses');
+      assignedConceptRule.value = new FshCode('foo');
+      const assignedRefRule = new AssignmentRule('addresses');
+      assignedRefRule.value = new FshReference('TestCondition');
+      carePlan.rules.push(assignedConceptRule, assignedRefRule);
+
+      doc.instances.set(condition.name, condition);
+      doc.instances.set(carePlan.name, carePlan);
+      const exported = exportInstance(carePlan);
+      expect(exported.addresses[0]).toEqual({
+        reference: { reference: 'Condition/condition-id' },
+        concept: {
+          coding: [{ code: 'foo' }]
+        }
+      });
     });
 
     // Assigning References
diff --git a/test/export/StructureDefinitionExporter.test.ts b/test/export/StructureDefinitionExporter.test.ts
index a5e6b44..78edecc 100644
--- a/test/export/StructureDefinitionExporter.test.ts
+++ b/test/export/StructureDefinitionExporter.test.ts
@@ -10348,6 +10348,49 @@ describe('StructureDefinitionExporter R5', () => {
     });
   });
 
+  it('should apply a Reference AssignmentRule directly to a CodeableReference element', () => {
+    const profile = new Profile('Foo');
+    profile.parent = 'CarePlan';
+
+    const instance = new Instance('Bar');
+    instance.id = 'bar-id';
+    instance.instanceOf = 'Condition';
+    doc.instances.set(instance.name, instance);
+
+    const rule = new AssignmentRule('addresses');
+    rule.value = new FshReference('Bar');
+    profile.rules.push(rule);
+
+    exporter.exportStructDef(profile);
+    const sd = pkg.profiles[0];
+
+    const addresses = sd.findElement('CarePlan.addresses');
+    expect(addresses.patternCodeableReference).toEqual({
+      reference: {
+        reference: 'Condition/bar-id'
+      }
+    });
+  });
+
+  it('should apply a FshCode AssignmentRule directly to a CodeableReference element', () => {
+    const profile = new Profile('Foo');
+    profile.parent = 'CarePlan';
+
+    const rule = new AssignmentRule('addresses');
+    rule.value = new FshCode('toast', 'http://bar.com');
+    profile.rules.push(rule);
+
+    exporter.exportStructDef(profile);
+    const sd = pkg.profiles[0];
+
+    const addresses = sd.findElement('CarePlan.addresses');
+    expect(addresses.patternCodeableReference).toEqual({
+      concept: {
+        coding: [{ code: 'toast', system: 'http://bar.com' }]
+      }
+    });
+  });
+
   it('should not apply a Reference AssignmentRule with invalid type constraints on a parent CodeableReference', () => {
     const profile = new Profile('Foo');
     profile.parent = 'CarePlan';
diff --git a/test/fhirtypes/ElementDefinition.assignFshCode.test.ts b/test/fhirtypes/ElementDefinition.assignFshCode.test.ts
index 2401a3c..87cfa52 100644
--- a/test/fhirtypes/ElementDefinition.assignFshCode.test.ts
+++ b/test/fhirtypes/ElementDefinition.assignFshCode.test.ts
@@ -41,7 +41,7 @@ describe('ElementDefinition', () => {
       expect(concept.fixedCodeableConcept).toBeUndefined();
     });
 
-    it('should assign a code to a CodeableConcept (exacty)', () => {
+    it('should assign a code to a CodeableConcept (exactly)', () => {
       const concept = observation.elements.find(e => e.id === 'Observation.code');
       concept.assignValue(fooBarCode, true);
       expect(concept.fixedCodeableConcept).toEqual({
@@ -837,5 +837,52 @@ describe('ElementDefinition', () => {
         'Cannot assign Code value on this element since this element does not have a single type'
       );
     });
+
+    describe('R5 CodeableReference', () => {
+      let r5Defs: FHIRDefinitions;
+      let r5Fisher: TestFisher;
+      let carePlan: StructureDefinition;
+
+      beforeAll(() => {
+        r5Defs = new FHIRDefinitions();
+        loadFromPath(
+          path.join(__dirname, '..', 'testhelpers', 'testdefs'),
+          'r5-definitions',
+          r5Defs
+        );
+        r5Fisher = new TestFisher().withFHIR(r5Defs);
+      });
+
+      beforeEach(() => {
+        carePlan = r5Fisher.fishForStructureDefinition('CarePlan');
+        loggerSpy.reset();
+      });
+
+      it('should assign a code to a CodeableReference', () => {
+        const addresses = carePlan.elements.find(e => e.id === 'CarePlan.addresses');
+        addresses.assignValue(fooBarCode);
+
+        expect(addresses.patternCodeableReference).toEqual({
+          concept: {
+            coding: [{ code: 'bar', system: 'http://foo.com' }]
+          }
+        });
+
+        expect(addresses.fixedCodeableReference).toBeUndefined();
+      });
+
+      it('should assign a code to a CodeableReference (exactly)', () => {
+        const addresses = carePlan.elements.find(e => e.id === 'CarePlan.addresses');
+        addresses.assignValue(fooBarCode, true);
+
+        expect(addresses.fixedCodeableReference).toEqual({
+          concept: {
+            coding: [{ code: 'bar', system: 'http://foo.com' }]
+          }
+        });
+
+        expect(addresses.patternCodeableReference).toBeUndefined();
+      });
+    });
   });
 });
diff --git a/test/fhirtypes/ElementDefinition.assignFshReference.test.ts b/test/fhirtypes/ElementDefinition.assignFshReference.test.ts
index 6657b44..bb105ab 100644
--- a/test/fhirtypes/ElementDefinition.assignFshReference.test.ts
+++ b/test/fhirtypes/ElementDefinition.assignFshReference.test.ts
@@ -2,8 +2,13 @@ import path from 'path';
 import { loadFromPath } from 'fhir-package-loader';
 import { FHIRDefinitions } from '../../src/fhirdefs/FHIRDefinitions';
 import { StructureDefinition } from '../../src/fhirtypes/StructureDefinition';
-import { FshReference } from '../../src/fshtypes';
+import { FshReference, Instance } from '../../src/fshtypes';
 import { TestFisher } from '../testhelpers';
+import { FSHDocument } from '../../src/import';
+import { minimalConfig } from '../utils/minimalConfig';
+import { FSHTank } from '../../src/import';
+import { Package } from '../../src/export';
+import { AssignmentRule } from '../../src/fshtypes/rules';
 
 describe('ElementDefinition', () => {
   let defs: FHIRDefinitions;
@@ -188,5 +193,82 @@ describe('ElementDefinition', () => {
         'Cannot assign Reference value: Reference(foo). Value does not match element type: code'
       );
     });
+
+    describe('R5 CodeableReference', () => {
+      let r5Defs: FHIRDefinitions;
+      let doc: FSHDocument;
+      let r5Fisher: TestFisher;
+      let carePlan: StructureDefinition;
+
+      beforeAll(() => {
+        r5Defs = new FHIRDefinitions();
+        loadFromPath(
+          path.join(__dirname, '..', 'testhelpers', 'testdefs'),
+          'r5-definitions',
+          r5Defs
+        );
+      });
+
+      beforeEach(() => {
+        doc = new FSHDocument('Conditions.fsh');
+        const input = new FSHTank([doc], minimalConfig);
+        const pkg = new Package(input.config);
+        r5Fisher = new TestFisher(input, r5Defs, pkg);
+        carePlan = r5Fisher.fishForStructureDefinition('CarePlan');
+        // we need an Instance of Condition to reference
+        const condition = new Instance('TestCondition');
+        condition.instanceOf = 'Condition';
+        const assignedIdRule = new AssignmentRule('id');
+        assignedIdRule.value = 'condition-id';
+        condition.rules.push(assignedIdRule);
+        doc.instances.set(condition.name, condition);
+      });
+
+      it('should assign a Reference to a CodeableReference', () => {
+        const addresses = carePlan.elements.find(e => e.id === 'CarePlan.addresses');
+        addresses.assignValue(new FshReference('condition-id'));
+        expect(addresses.patternCodeableReference).toEqual({
+          reference: {
+            reference: 'condition-id'
+          }
+        });
+        expect(addresses.fixedCodeableReference).toBeUndefined();
+      });
+
+      it('should assign a Reference with a display to a CodeableReference', () => {
+        const addresses = carePlan.elements.find(e => e.id === 'CarePlan.addresses');
+        addresses.assignValue(new FshReference('condition-id', 'Condition ID'));
+        expect(addresses.patternCodeableReference).toEqual({
+          reference: {
+            reference: 'condition-id',
+            display: 'Condition ID'
+          }
+        });
+        expect(addresses.fixedCodeableReference).toBeUndefined();
+      });
+
+      it('should assign a Reference to a CodeableReference (exactly)', () => {
+        const addresses = carePlan.elements.find(e => e.id === 'CarePlan.addresses');
+        addresses.assignValue(new FshReference('condition-id'), true);
+        expect(addresses.fixedCodeableReference).toEqual({
+          reference: {
+            reference: 'condition-id'
+          }
+        });
+        expect(addresses.patternCodeableReference).toBeUndefined();
+      });
+
+      it('should assign a Reference with a display to a CodeableReference (exactly)', () => {
+        const addresses = carePlan.elements.find(e => e.id === 'CarePlan.addresses');
+        addresses.assignValue(new FshReference('condition-id', 'Condition ID'), true);
+        expect(addresses.fixedCodeableReference).toEqual({
+          reference: {
+            reference: 'condition-id',
+            display: 'Condition ID'
+          }
+        });
+        expect(addresses.patternCodeableReference).toBeUndefined();
+      });
+    });
   });
 });
diff --git a/test/fhirtypes/ElementDefinition.assignValue.test.ts b/test/fhirtypes/ElementDefinition.assignValue.test.ts
index f7fd585..103bb1f 100644
--- a/test/fhirtypes/ElementDefinition.assignValue.test.ts
+++ b/test/fhirtypes/ElementDefinition.assignValue.test.ts
@@ -4,6 +4,11 @@ import { FHIRDefinitions } from '../../src/fhirdefs/FHIRDefinitions';
 import { StructureDefinition } from '../../src/fhirtypes/StructureDefinition';
 import { FshCode } from '../../src/fshtypes/FshCode';
 import { TestFisher } from '../testhelpers';
+import { Package } from '../../src/export';
+import { FshReference, Instance } from '../../src/fshtypes';
+import { AssignmentRule } from '../../src/fshtypes/rules';
+import { FSHDocument, FSHTank } from '../../src/import';
+import { minimalConfig } from '../utils/minimalConfig';
 
 describe('ElementDefinition', () => {
   let defs: FHIRDefinitions;
@@ -146,6 +151,93 @@ describe('ElementDefinition', () => {
       expect(catMouseCoding.patternCoding).toEqual({ code: 'cheese' });
       expect(catMouseCoding.min).toBe(0); // We do not increase min, since no discriminator
     });
+
+    describe('R5 CodeableReference', () => {
+      let r5Defs: FHIRDefinitions;
+      let doc: FSHDocument;
+      let r5Fisher: TestFisher;
+      let carePlan: StructureDefinition;
+
+      beforeAll(() => {
+        r5Defs = new FHIRDefinitions();
+        loadFromPath(
+          path.join(__dirname, '..', 'testhelpers', 'testdefs'),
+          'r5-definitions',
+          r5Defs
+        );
+      });
+
+      beforeEach(() => {
+        doc = new FSHDocument('Conditions.fsh');
+        const input = new FSHTank([doc], minimalConfig);
+        const pkg = new Package(input.config);
+        r5Fisher = new TestFisher(input, r5Defs, pkg);
+        carePlan = r5Fisher.fishForStructureDefinition('CarePlan');
+        // we need an Instance of Condition to reference
+        const condition = new Instance('TestCondition');
+        condition.instanceOf = 'Condition';
+        const assignedIdRule = new AssignmentRule('id');
+        assignedIdRule.value = 'condition-id';
+        condition.rules.push(assignedIdRule);
+        doc.instances.set(condition.name, condition);
+      });
+
+      it('should assign a patternCodeableReference with a code on an element with an existing patternCodeableReference with a reference', () => {
+        const addresses = carePlan.elements.find(e => e.id === 'CarePlan.addresses');
+        addresses.assignValue(new FshReference('condition-id'));
+        addresses.assignValue(new FshCode('bar', 'http://foo.com'));
+        expect(addresses.patternCodeableReference).toEqual({
+          concept: {
+            coding: [{ code: 'bar', system: 'http://foo.com' }]
+          },
+          reference: {
+            reference: 'condition-id'
+          }
+        });
+      });
+
+      it('should assign a patternCodeableReference with a Reference on an element with an existing patternCodeableReference with a concept', () => {
+        const addresses = carePlan.elements.find(e => e.id === 'CarePlan.addresses');
+        addresses.assignValue(new FshCode('bar', 'http://foo.com'));
+        addresses.assignValue(new FshReference('condition-id'));
+        expect(addresses.patternCodeableReference).toEqual({
+          concept: {
+            coding: [{ code: 'bar', system: 'http://foo.com' }]
+          },
+          reference: {
+            reference: 'condition-id'
+          }
+        });
+      });
+
+      it('should assign a fixedCodeableReference with a code on an element with an existing fixedCodeableReference with a reference', () => {
+        const addresses = carePlan.elements.find(e => e.id === 'CarePlan.addresses');
+        addresses.assignValue(new FshReference('condition-id'), true);
+        addresses.assignValue(new FshCode('bar', 'http://foo.com'), true);
+        expect(addresses.fixedCodeableReference).toEqual({
+          concept: {
+            coding: [{ code: 'bar', system: 'http://foo.com' }]
+          },
+          reference: {
+            reference: 'condition-id'
+          }
+        });
+      });
+
+      it('should assign a fixedCodeableReference with a Reference on an element with an existing fixedCodeableReference with a concept', () => {
+        const addresses = carePlan.elements.find(e => e.id === 'CarePlan.addresses');
+        addresses.assignValue(new FshCode('bar', 'http://foo.com'), true);
+        addresses.assignValue(new FshReference('condition-id'), true);
+        expect(addresses.fixedCodeableReference).toEqual({
+          concept: {
+            coding: [{ code: 'bar', system: 'http://foo.com' }]
+          },
+          reference: {
+            reference: 'condition-id'
+          }
+        });
+      });
+    });
   });
 
   describe('#assignedByDirectParent', () => {