@@ -120,8 +120,47 @@ private static Questionnaire createEmptyQuestionnaire() {
120120 return questionnaire ;
121121 }
122122
123+ /**
124+ * Creates a Patient with all fields needed by the patient projection tests: two names, gender,
125+ * marital status, and nested extensions.
126+ *
127+ * <p>Extension structure:
128+ *
129+ * <pre>
130+ * Patient
131+ * name: Smith, Jones
132+ * gender: MALE
133+ * maritalStatus: M (Married)
134+ * extension "http://example.com/simple" = "simple-value"
135+ * extension "http://example.com/parent"
136+ * extension "http://example.com/child" = "child-value"
137+ * </pre>
138+ *
139+ * @return a Patient resource with all fields populated
140+ */
141+ private static Patient createPatient () {
142+ final Patient patient = new Patient ();
143+ patient .setId ("test-patient" );
144+ patient .addName ().setFamily ("Smith" );
145+ patient .addName ().setFamily ("Jones" );
146+ patient .setGender (AdministrativeGender .MALE );
147+ patient .setMaritalStatus (
148+ new CodeableConcept (new Coding ("http://example.com/cs" , "M" , "Married" )));
149+
150+ // Simple leaf extension.
151+ patient .addExtension (
152+ new Extension ("http://example.com/simple" , new StringType ("simple-value" )));
153+
154+ // Complex extension containing a nested sub-extension.
155+ final Extension parent = new Extension ("http://example.com/parent" );
156+ parent .addExtension (new Extension ("http://example.com/child" , new StringType ("child-value" )));
157+ patient .addExtension (parent );
158+
159+ return patient ;
160+ }
161+
123162 @ FhirPathTest
124- public Stream <DynamicTest > testRepeatAllBasicTraversal () {
163+ public Stream <DynamicTest > testRepeatAllQuestionnaireTraversal () {
125164 return builder ()
126165 .withResource (createQuestionnaire ())
127166 .group ("repeatAll() basic traversal" )
@@ -133,18 +172,54 @@ public Stream<DynamicTest> testRepeatAllBasicTraversal() {
133172 4 ,
134173 "repeatAll(item).count()" ,
135174 "repeatAll(item).count() returns total items across all levels" )
175+ .group ("repeatAll() with filtering" )
176+ .testEquals (
177+ List .of ("1" , "1.1" ),
178+ "repeatAll(item).where(type = 'group').linkId" ,
179+ "repeatAll(item).where(type = 'group') filters items from all nesting levels" )
180+ .group ("repeatAll() $index not defined" )
181+ .testError (
182+ "repeatAll($index)" , "$index is not available within repeatAll projection expression" )
136183 .build ();
137184 }
138185
139186 @ FhirPathTest
140- public Stream <DynamicTest > testRepeatAllFiltering () {
187+ public Stream <DynamicTest > testRepeatAllPatientProjections () {
188+ // repeatAll(first()) produces the same SQL DataType at every level (ArrayType(HumanName)),
189+ // triggering same-type depth limiting. With a depth limit of 10, the initial level (where
190+ // parentType is None and does not consume budget) plus 10 same-type levels produce 11 total
191+ // copies of the first element before traversal stops.
141192 return builder ()
142- .withResource (createQuestionnaire ())
143- .group ("repeatAll() with filtering " )
193+ .withResource (createPatient ())
194+ .group ("repeatAll() non-recursive projection " )
144195 .testEquals (
145- List .of ("1" , "1.1" ),
146- "repeatAll(item).where(type = 'group').linkId" ,
147- "repeatAll(item).where(type = 'group') filters items from all nesting levels" )
196+ List .of ("Smith" , "Jones" ),
197+ "repeatAll(name).family" ,
198+ "repeatAll(name).family returns same result as select() for non-recursive fields" )
199+ .group ("repeatAll() singular primitive projection" )
200+ .testEquals (
201+ "male" ,
202+ "repeatAll(gender)" ,
203+ "repeatAll(gender) returns a singular primitive value like select()" )
204+ .group ("repeatAll() singular complex projection" )
205+ .testEquals (
206+ "M" ,
207+ "repeatAll(maritalStatus).coding.code" ,
208+ "repeatAll(maritalStatus) returns a singular complex value with sub-elements intact" )
209+ .group ("repeatAll() identity-like same-type depth limiting" )
210+ .testEquals (
211+ 11 ,
212+ "name.repeatAll(first()).count()" ,
213+ "repeatAll(first()) stops after same-type depth limit and returns bounded results" )
214+ .group ("repeatAll() extension traversal" )
215+ .testEquals (
216+ List .of (
217+ "http://example.com/simple" ,
218+ "http://example.com/parent" ,
219+ "http://example.com/child" ),
220+ "repeatAll(extension).url" ,
221+ "repeatAll(extension) recursively collects all extensions including nested"
222+ + " sub-extensions" )
148223 .build ();
149224 }
150225
@@ -157,6 +232,10 @@ public Stream<DynamicTest> testRepeatAllEmptyInput() {
157232 "repeatAll(item)" , "repeatAll() on a resource with no items returns empty collection" )
158233 .testEquals (
159234 0 , "repeatAll(item).count()" , "repeatAll(item).count() returns 0 for empty input" )
235+ .group ("repeatAll() empty literal" )
236+ .testEmpty (
237+ "{}.repeatAll($this)" ,
238+ "repeatAll() on the FHIRPath empty literal returns empty collection" )
160239 .build ();
161240 }
162241
@@ -231,56 +310,6 @@ public Stream<DynamicTest> testRepeatAllDuplicatesPreserved() {
231310 .build ();
232311 }
233312
234- @ FhirPathTest
235- public Stream <DynamicTest > testRepeatAllNonRecursiveProjection () {
236- final Patient patient = new Patient ();
237- patient .setId ("test-patient" );
238- patient .addName ().setFamily ("Smith" );
239- patient .addName ().setFamily ("Jones" );
240-
241- return builder ()
242- .withResource (patient )
243- .group ("repeatAll() non-recursive projection" )
244- .testEquals (
245- List .of ("Smith" , "Jones" ),
246- "repeatAll(name).family" ,
247- "repeatAll(name).family returns same result as select() for non-recursive fields" )
248- .build ();
249- }
250-
251- @ FhirPathTest
252- public Stream <DynamicTest > testRepeatAllSingularPrimitive () {
253- final Patient patient = new Patient ();
254- patient .setId ("singular-patient" );
255- patient .setGender (AdministrativeGender .MALE );
256-
257- return builder ()
258- .withResource (patient )
259- .group ("repeatAll() singular primitive projection" )
260- .testEquals (
261- "male" ,
262- "repeatAll(gender)" ,
263- "repeatAll(gender) returns a singular primitive value like select()" )
264- .build ();
265- }
266-
267- @ FhirPathTest
268- public Stream <DynamicTest > testRepeatAllSingularComplex () {
269- final Patient patient = new Patient ();
270- patient .setId ("complex-patient" );
271- patient .setMaritalStatus (
272- new CodeableConcept (new Coding ("http://example.com/cs" , "M" , "Married" )));
273-
274- return builder ()
275- .withResource (patient )
276- .group ("repeatAll() singular complex projection" )
277- .testEquals (
278- "M" ,
279- "repeatAll(maritalStatus).coding.code" ,
280- "repeatAll(maritalStatus) returns a singular complex value with sub-elements intact" )
281- .build ();
282- }
283-
284313 /**
285314 * Creates a QuestionnaireResponse with items nested through answer elements, to verify that
286315 * repeatAll can traverse through intermediate types (Item -> Answer -> Item).
0 commit comments