001package ca.uhn.fhir.jpa.conformance; 002 003/*- 004 * #%L 005 * HAPI FHIR Test Utilities 006 * %% 007 * Copyright (C) 2014 - 2023 Smile CDR, Inc. 008 * %% 009 * Licensed under the Apache License, Version 2.0 (the "License"); 010 * you may not use this file except in compliance with the License. 011 * You may obtain a copy of the License at 012 * 013 * http://www.apache.org/licenses/LICENSE-2.0 014 * 015 * Unless required by applicable law or agreed to in writing, software 016 * distributed under the License is distributed on an "AS IS" BASIS, 017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 018 * See the License for the specific language governing permissions and 019 * limitations under the License. 020 * #L% 021 */ 022 023import ca.uhn.fhir.util.CollectionUtil; 024import org.junit.jupiter.params.provider.Arguments; 025 026import javax.annotation.Nonnull; 027import java.io.IOException; 028import java.io.InputStream; 029import java.io.InputStreamReader; 030import java.io.LineNumberReader; 031import java.io.Reader; 032import java.nio.charset.StandardCharsets; 033import java.util.ArrayList; 034import java.util.Arrays; 035import java.util.List; 036import java.util.Set; 037import java.util.stream.Collectors; 038 039/** 040 * Collection of test cases for date type search. 041 * 042 * Each test case includes a resource value, a query value, the operator to test, and the expected result. 043 * 044 * @see <a href="https://www.hl7.org/fhir/search.html#date">the spec</a> 045 */ 046public class DateSearchTestCase { 047 final String myResourceValue; 048 final String myQueryValue; 049 final boolean expectedResult; 050 final String myFileName; 051 final int myLineNumber; 052 053 public DateSearchTestCase(String myResourceValue, String myQueryValue, boolean expectedResult, String theFileName, int theLineNumber) { 054 this.myResourceValue = myResourceValue; 055 this.myQueryValue = myQueryValue; 056 this.expectedResult = expectedResult; 057 this.myFileName = theFileName; 058 this.myLineNumber = theLineNumber; 059 } 060 061 public Arguments toArguments() { 062 return Arguments.of(myResourceValue, myQueryValue, expectedResult, myFileName, myLineNumber); 063 } 064 065 /** 066 * We have two sources of test cases: 067 * - DateSearchTestCase.csv which holds one test case per line 068 * - DateSearchTestCase-compact.csv which specifies all operators for each value pair 069 */ 070 public final static List<DateSearchTestCase> ourCases; 071 static { 072 ourCases = new ArrayList<>(); 073 ourCases.addAll(expandedCases()); 074 ourCases.addAll(compactCases()); 075 } 076 077 private static List<DateSearchTestCase> expandedCases() { 078 String csv = "DateSearchTestCase.csv"; 079 InputStream resource = DateSearchTestCase.class.getResourceAsStream(csv); 080 assert resource != null; 081 InputStreamReader inputStreamReader = new InputStreamReader(resource, StandardCharsets.UTF_8); 082 List<DateSearchTestCase> cases = parseCsvCases(inputStreamReader, csv); 083 try { 084 resource.close(); 085 } catch (IOException e) { 086 e.printStackTrace(); 087 } 088 return cases; 089 } 090 091 static List<DateSearchTestCase> parseCsvCases(Reader theSource, String theFileName) { 092 LineNumberReader lineNumberReader = new LineNumberReader(theSource); 093 return lineNumberReader.lines() 094 .filter(l->!l.startsWith("#")) // strip comments 095 .map(l -> l.split(",")) 096 .map(fields -> new DateSearchTestCase(fields[0].trim(), fields[1].trim(), Boolean.parseBoolean(fields[2].trim()), theFileName, lineNumberReader.getLineNumber())) 097 .collect(Collectors.toList()); 098 } 099 100 public static List<DateSearchTestCase> compactCases() { 101 String compactCsv = "DateSearchTestCase-compact.csv"; 102 InputStream compactStream = DateSearchTestCase.class.getResourceAsStream(compactCsv); 103 assert compactStream != null; 104 return expandPrefixCases(new InputStreamReader(compactStream, StandardCharsets.UTF_8), compactCsv); 105 } 106 107 /** 108 * helper for compressed format of date test cases. 109 * <p> 110 * The csv has rows with: Matching prefixes, Query Date, Resource Date 111 * E.g. "eq ge le,2020, 2020" 112 * This helper expands that one line into test for all of: eq, ge, gt, le, lt, and ne, 113 * expecting the listed prefixes to match, and the unlisted ones to not match. 114 * 115 * @return List of test cases 116 */ 117 @Nonnull 118 static List<DateSearchTestCase> expandPrefixCases(Reader theSource, String theFileName) { 119 Set<String> supportedPrefixes = CollectionUtil.newSet("eq", "ge", "gt", "le", "lt", "ne"); 120 121 // expand these into individual tests for each prefix. 122 LineNumberReader lineNumberReader = new LineNumberReader(theSource); 123 return lineNumberReader.lines() 124 .filter(l->!l.startsWith("#")) // strip comments 125 .map(l -> l.split(",")) 126 .flatMap(fields -> { 127 // line looks like: "eq ge le,2020, 2020" 128 // Matching prefixes, Query Date, Resource Date 129 String resourceValue = fields[0].trim(); 130 String truePrefixes = fields[1].trim(); 131 String queryValue = fields[2].trim(); 132 Set<String> expectedTruePrefixes = Arrays.stream(truePrefixes.split("\\s+")).map(String::trim).collect(Collectors.toSet()); 133 134 // expand to one test case per supportedPrefixes 135 return supportedPrefixes.stream() 136 .map(prefix -> { 137 boolean expectMatch = expectedTruePrefixes.contains(prefix); 138 return new DateSearchTestCase(resourceValue, prefix + queryValue, expectMatch, theFileName, lineNumberReader.getLineNumber()); 139 }); 140 }) 141 .collect(Collectors.toList()); 142 } 143}