diff --git a/.github/workflows/cs-comparison.yml b/.github/workflows/cs-comparison.yml
index f66589076..8b7883fb6 100644
--- a/.github/workflows/cs-comparison.yml
+++ b/.github/workflows/cs-comparison.yml
@@ -25,9 +25,7 @@ jobs:
restore-keys: |
${{ runner.os }}-maven-
- # Run the comparison using the test classpath
- - name: Build and run CS Comparison
+ - name: Build and run CS comparison
run: |
chmod +x ./mvnw
- ./mvnw -B test-compile -DskipTests
- ./mvnw -B exec:java -DskipTests -Dexec.mainClass=com.imsweb.staging.cs.CsIntegrationTest -Dexec.classpathScope=test
+ ./mvnw -B -Pcs-comparison verify
diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml
index c3d002456..b99e60496 100644
--- a/.github/workflows/integration.yml
+++ b/.github/workflows/integration.yml
@@ -44,6 +44,13 @@ jobs:
chmod +x ./mvnw
./mvnw -B verify
+ - name: Upload JaCoCo report
+ if: always()
+ uses: actions/upload-artifact@v4
+ with:
+ name: jacoco-report
+ path: target/site/jacoco/
+
- name: SonarCloud Analysis
if: github.actor != 'dependabot[bot]'
env:
diff --git a/.gitignore b/.gitignore
index a5edded5c..8b2a01285 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,6 +2,7 @@ HELP.md
target/
!**/src/main/**/target/
!**/src/test/**/target/
+node_modules/
### STS ###
.apt_generated
@@ -30,4 +31,5 @@ build/
### VS Code ###
.vscode/
-
+package-lock.json
+package.json
diff --git a/pom.xml b/pom.xml
index ece3c9ebb..fc0cd4b56 100644
--- a/pom.xml
+++ b/pom.xml
@@ -156,7 +156,7 @@
org.jacoco
jacoco-maven-plugin
- 0.8.12
+ 0.8.15
prepare-agent
@@ -171,6 +171,32 @@
report
+
+ check
+ verify
+
+ check
+
+
+
+
+ BUNDLE
+
+
+ LINE
+ COVEREDRATIO
+ 0.95
+
+
+ BRANCH
+ COVEREDRATIO
+ 0.89
+
+
+
+
+
+
@@ -241,6 +267,64 @@
+
+ cs-comparison
+
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+ 3.6.1
+
+
+ add-cs-comparison-test-source
+ generate-test-sources
+
+ add-test-source
+
+
+
+ src/cs-comparison/java
+
+
+
+
+ add-cs-comparison-test-resource
+ generate-test-resources
+
+ add-test-resource
+
+
+
+
+ src/cs-comparison/resources
+
+
+
+
+
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+ 3.6.3
+
+
+ run-cs-comparison
+ integration-test
+
+ java
+
+
+ com.imsweb.staging.cs.CsComparisonRunner
+ test
+
+
+
+
+
+
+
release
diff --git a/src/test/java/com/imsweb/staging/cs/CsIntegrationTest.java b/src/cs-comparison/java/com/imsweb/staging/cs/CsComparisonRunner.java
similarity index 52%
rename from src/test/java/com/imsweb/staging/cs/CsIntegrationTest.java
rename to src/cs-comparison/java/com/imsweb/staging/cs/CsComparisonRunner.java
index 988c7bc2e..809dc5fef 100644
--- a/src/test/java/com/imsweb/staging/cs/CsIntegrationTest.java
+++ b/src/cs-comparison/java/com/imsweb/staging/cs/CsComparisonRunner.java
@@ -3,6 +3,12 @@
*/
package com.imsweb.staging.cs;
+import static com.imsweb.staging.StagingTest.getAlgorithmPath;
+
+import com.imsweb.staging.ExternalStagingFileDataProvider;
+import com.imsweb.staging.Staging;
+import com.imsweb.staging.cs.IntegrationUtils.IntegrationResult;
+import com.imsweb.staging.util.Stopwatch;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
@@ -15,21 +21,12 @@
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.zip.GZIPInputStream;
-
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.imsweb.staging.ExternalStagingFileDataProvider;
-import com.imsweb.staging.Staging;
-import com.imsweb.staging.cs.IntegrationUtils.IntegrationResult;
-import com.imsweb.staging.util.Stopwatch;
+public class CsComparisonRunner {
-import static com.imsweb.staging.StagingTest.getAlgorithmPath;
-
-@SuppressWarnings("java:S2187")
-public class CsIntegrationTest {
-
- private static final Logger _LOG = LoggerFactory.getLogger(CsIntegrationTest.class);
+ private static final Logger _LOG = LoggerFactory.getLogger(CsComparisonRunner.class);
// set this to null to process all, or a list of schema filename to process
private static final List _SCHEMA_FILES = Collections.emptyList();
@@ -43,8 +40,17 @@ private static void execute() throws IOException, InterruptedException, URISynta
// only do schema selection test if running all schemas
if (_SCHEMA_FILES.isEmpty()) {
- IntegrationUtils.processSchemaSelection(staging, "cs_schema_identification.txt.gz",
- new GZIPInputStream(Objects.requireNonNull(Thread.currentThread().getContextClassLoader().getResourceAsStream("cs/integration/schema_selection/cs_schema_identification.txt.gz"))));
+ IntegrationUtils.processSchemaSelection(
+ staging,
+ "cs_schema_identification.txt.gz",
+ new GZIPInputStream(
+ Objects.requireNonNull(
+ Thread.currentThread()
+ .getContextClassLoader()
+ .getResourceAsStream("cs/integration/schema_selection/cs_schema_identification.txt.gz")
+ )
+ )
+ );
_LOG.info("-----------------------------------------------");
}
@@ -55,8 +61,18 @@ private static void execute() throws IOException, InterruptedException, URISynta
// NOTE: some of these files are REALLY large; so they are not included in the project for now; all files larger than 2MB (40 of them) are not
// part of the repository and can be found at \\omni\btp\csb\Staging\CS
List schemaFiles;
- try (BufferedReader buffer = new BufferedReader(
- new InputStreamReader(Objects.requireNonNull(Thread.currentThread().getContextClassLoader().getResourceAsStream("cs/integration/schemas/index.txt")), StandardCharsets.UTF_8))) {
+ try (
+ BufferedReader buffer = new BufferedReader(
+ new InputStreamReader(
+ Objects.requireNonNull(
+ Thread.currentThread()
+ .getContextClassLoader()
+ .getResourceAsStream("cs/integration/schemas/index.txt")
+ ),
+ StandardCharsets.UTF_8
+ )
+ )
+ ) {
schemaFiles = buffer.lines().toList();
}
@@ -67,8 +83,17 @@ private static void execute() throws IOException, InterruptedException, URISynta
for (String schemaFile : schemaFiles) {
if (_SCHEMA_FILES.isEmpty() || _SCHEMA_FILES.contains(schemaFile)) {
totalFiles += 1;
- IntegrationResult result = IntegrationUtils.processSchema(staging, schemaFile,
- new GZIPInputStream(Objects.requireNonNull(Thread.currentThread().getContextClassLoader().getResourceAsStream("cs/integration/schemas/" + schemaFile))));
+ IntegrationResult result = IntegrationUtils.processSchema(
+ staging,
+ schemaFile,
+ new GZIPInputStream(
+ Objects.requireNonNull(
+ Thread.currentThread()
+ .getContextClassLoader()
+ .getResourceAsStream("cs/integration/schemas/" + schemaFile)
+ )
+ )
+ );
totalCases += result.getNumCases();
totalFailures += result.getNumFailures();
}
@@ -76,11 +101,27 @@ private static void execute() throws IOException, InterruptedException, URISynta
stopwatch.stop();
- String perMs = String.format("%.3f", ((float)stopwatch.elapsed(TimeUnit.MILLISECONDS) / totalCases));
+ String perMs = String.format("%.3f", (float) stopwatch.elapsed(TimeUnit.MILLISECONDS) / totalCases);
_LOG.info("");
- _LOG.info("Completed {} cases ({} files) in {} ({}ms/case).", NumberFormat.getNumberInstance(Locale.US).format(totalCases), totalFiles, stopwatch, perMs);
- if (totalFailures > 0)
- _LOG.error("There were {} failing cases.", NumberFormat.getNumberInstance(Locale.US).format(totalFailures));
+ _LOG.info(
+ "Completed {} cases ({} files) in {} ({}ms/case).",
+ NumberFormat.getNumberInstance(Locale.US).format(totalCases),
+ totalFiles,
+ stopwatch,
+ perMs
+ );
+ if (totalFailures > 0) _LOG.error(
+ "There were {} failing cases.",
+ NumberFormat.getNumberInstance(Locale.US).format(totalFailures)
+ );
+ failIfNecessary(totalFailures);
}
+ static void failIfNecessary(long totalFailures) {
+ if (totalFailures > 0) throw new IllegalStateException(
+ "CS comparison failed with " +
+ NumberFormat.getNumberInstance(Locale.US).format(totalFailures) +
+ " failing cases."
+ );
+ }
}
diff --git a/src/cs-comparison/java/com/imsweb/staging/cs/CsComparisonRunnerTest.java b/src/cs-comparison/java/com/imsweb/staging/cs/CsComparisonRunnerTest.java
new file mode 100644
index 000000000..3bd0e66e5
--- /dev/null
+++ b/src/cs-comparison/java/com/imsweb/staging/cs/CsComparisonRunnerTest.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2026 Information Management Services, Inc.
+ */
+package com.imsweb.staging.cs;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import org.junit.jupiter.api.Test;
+
+class CsComparisonRunnerTest {
+
+ @Test
+ void testNoFailures() {
+ assertDoesNotThrow(() -> CsComparisonRunner.failIfNecessary(0));
+ }
+
+ @Test
+ void testFailures() {
+ IllegalStateException exception = assertThrows(IllegalStateException.class, () ->
+ CsComparisonRunner.failIfNecessary(1234)
+ );
+ assertEquals("CS comparison failed with 1,234 failing cases.", exception.getMessage());
+ }
+}
diff --git a/src/test/resources/cs/integration/schema_selection/cs_schema_identification.txt.gz b/src/cs-comparison/resources/cs/integration/schema_selection/cs_schema_identification.txt.gz
similarity index 100%
rename from src/test/resources/cs/integration/schema_selection/cs_schema_identification.txt.gz
rename to src/cs-comparison/resources/cs/integration/schema_selection/cs_schema_identification.txt.gz
diff --git a/src/test/resources/cs/integration/schemas/adnexa_uterine_other.gz b/src/cs-comparison/resources/cs/integration/schemas/adnexa_uterine_other.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/adnexa_uterine_other.gz
rename to src/cs-comparison/resources/cs/integration/schemas/adnexa_uterine_other.gz
diff --git a/src/test/resources/cs/integration/schemas/adrenal_gland.gz b/src/cs-comparison/resources/cs/integration/schemas/adrenal_gland.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/adrenal_gland.gz
rename to src/cs-comparison/resources/cs/integration/schemas/adrenal_gland.gz
diff --git a/src/test/resources/cs/integration/schemas/ampulla_vater.gz b/src/cs-comparison/resources/cs/integration/schemas/ampulla_vater.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/ampulla_vater.gz
rename to src/cs-comparison/resources/cs/integration/schemas/ampulla_vater.gz
diff --git a/src/test/resources/cs/integration/schemas/anus.gz b/src/cs-comparison/resources/cs/integration/schemas/anus.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/anus.gz
rename to src/cs-comparison/resources/cs/integration/schemas/anus.gz
diff --git a/src/test/resources/cs/integration/schemas/bile_ducts_distal.gz b/src/cs-comparison/resources/cs/integration/schemas/bile_ducts_distal.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/bile_ducts_distal.gz
rename to src/cs-comparison/resources/cs/integration/schemas/bile_ducts_distal.gz
diff --git a/src/test/resources/cs/integration/schemas/bile_ducts_perihilar.gz b/src/cs-comparison/resources/cs/integration/schemas/bile_ducts_perihilar.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/bile_ducts_perihilar.gz
rename to src/cs-comparison/resources/cs/integration/schemas/bile_ducts_perihilar.gz
diff --git a/src/test/resources/cs/integration/schemas/biliary_other.gz b/src/cs-comparison/resources/cs/integration/schemas/biliary_other.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/biliary_other.gz
rename to src/cs-comparison/resources/cs/integration/schemas/biliary_other.gz
diff --git a/src/test/resources/cs/integration/schemas/brain.gz b/src/cs-comparison/resources/cs/integration/schemas/brain.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/brain.gz
rename to src/cs-comparison/resources/cs/integration/schemas/brain.gz
diff --git a/src/test/resources/cs/integration/schemas/buccal_mucosa.gz b/src/cs-comparison/resources/cs/integration/schemas/buccal_mucosa.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/buccal_mucosa.gz
rename to src/cs-comparison/resources/cs/integration/schemas/buccal_mucosa.gz
diff --git a/src/test/resources/cs/integration/schemas/cervix.gz b/src/cs-comparison/resources/cs/integration/schemas/cervix.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/cervix.gz
rename to src/cs-comparison/resources/cs/integration/schemas/cervix.gz
diff --git a/src/test/resources/cs/integration/schemas/cns_other.gz b/src/cs-comparison/resources/cs/integration/schemas/cns_other.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/cns_other.gz
rename to src/cs-comparison/resources/cs/integration/schemas/cns_other.gz
diff --git a/src/test/resources/cs/integration/schemas/conjunctiva.gz b/src/cs-comparison/resources/cs/integration/schemas/conjunctiva.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/conjunctiva.gz
rename to src/cs-comparison/resources/cs/integration/schemas/conjunctiva.gz
diff --git a/src/test/resources/cs/integration/schemas/corpus_adenosarcoma.gz b/src/cs-comparison/resources/cs/integration/schemas/corpus_adenosarcoma.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/corpus_adenosarcoma.gz
rename to src/cs-comparison/resources/cs/integration/schemas/corpus_adenosarcoma.gz
diff --git a/src/test/resources/cs/integration/schemas/cystic_duct.gz b/src/cs-comparison/resources/cs/integration/schemas/cystic_duct.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/cystic_duct.gz
rename to src/cs-comparison/resources/cs/integration/schemas/cystic_duct.gz
diff --git a/src/test/resources/cs/integration/schemas/digestive_other.gz b/src/cs-comparison/resources/cs/integration/schemas/digestive_other.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/digestive_other.gz
rename to src/cs-comparison/resources/cs/integration/schemas/digestive_other.gz
diff --git a/src/test/resources/cs/integration/schemas/endocrine_other.gz b/src/cs-comparison/resources/cs/integration/schemas/endocrine_other.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/endocrine_other.gz
rename to src/cs-comparison/resources/cs/integration/schemas/endocrine_other.gz
diff --git a/src/test/resources/cs/integration/schemas/epiglottis_anterior.gz b/src/cs-comparison/resources/cs/integration/schemas/epiglottis_anterior.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/epiglottis_anterior.gz
rename to src/cs-comparison/resources/cs/integration/schemas/epiglottis_anterior.gz
diff --git a/src/test/resources/cs/integration/schemas/eye_other.gz b/src/cs-comparison/resources/cs/integration/schemas/eye_other.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/eye_other.gz
rename to src/cs-comparison/resources/cs/integration/schemas/eye_other.gz
diff --git a/src/test/resources/cs/integration/schemas/fallopian_tube.gz b/src/cs-comparison/resources/cs/integration/schemas/fallopian_tube.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/fallopian_tube.gz
rename to src/cs-comparison/resources/cs/integration/schemas/fallopian_tube.gz
diff --git a/src/test/resources/cs/integration/schemas/floor_mouth.gz b/src/cs-comparison/resources/cs/integration/schemas/floor_mouth.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/floor_mouth.gz
rename to src/cs-comparison/resources/cs/integration/schemas/floor_mouth.gz
diff --git a/src/test/resources/cs/integration/schemas/gallbladder.gz b/src/cs-comparison/resources/cs/integration/schemas/gallbladder.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/gallbladder.gz
rename to src/cs-comparison/resources/cs/integration/schemas/gallbladder.gz
diff --git a/src/test/resources/cs/integration/schemas/genital_female_other.gz b/src/cs-comparison/resources/cs/integration/schemas/genital_female_other.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/genital_female_other.gz
rename to src/cs-comparison/resources/cs/integration/schemas/genital_female_other.gz
diff --git a/src/test/resources/cs/integration/schemas/genital_male_other.gz b/src/cs-comparison/resources/cs/integration/schemas/genital_male_other.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/genital_male_other.gz
rename to src/cs-comparison/resources/cs/integration/schemas/genital_male_other.gz
diff --git a/src/test/resources/cs/integration/schemas/gist_appendix.gz b/src/cs-comparison/resources/cs/integration/schemas/gist_appendix.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/gist_appendix.gz
rename to src/cs-comparison/resources/cs/integration/schemas/gist_appendix.gz
diff --git a/src/test/resources/cs/integration/schemas/gist_colon.gz b/src/cs-comparison/resources/cs/integration/schemas/gist_colon.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/gist_colon.gz
rename to src/cs-comparison/resources/cs/integration/schemas/gist_colon.gz
diff --git a/src/test/resources/cs/integration/schemas/gist_esophagus.gz b/src/cs-comparison/resources/cs/integration/schemas/gist_esophagus.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/gist_esophagus.gz
rename to src/cs-comparison/resources/cs/integration/schemas/gist_esophagus.gz
diff --git a/src/test/resources/cs/integration/schemas/gist_peritoneum.gz b/src/cs-comparison/resources/cs/integration/schemas/gist_peritoneum.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/gist_peritoneum.gz
rename to src/cs-comparison/resources/cs/integration/schemas/gist_peritoneum.gz
diff --git a/src/test/resources/cs/integration/schemas/gist_rectum.gz b/src/cs-comparison/resources/cs/integration/schemas/gist_rectum.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/gist_rectum.gz
rename to src/cs-comparison/resources/cs/integration/schemas/gist_rectum.gz
diff --git a/src/test/resources/cs/integration/schemas/gist_small_intestine.gz b/src/cs-comparison/resources/cs/integration/schemas/gist_small_intestine.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/gist_small_intestine.gz
rename to src/cs-comparison/resources/cs/integration/schemas/gist_small_intestine.gz
diff --git a/src/test/resources/cs/integration/schemas/gist_stomach.gz b/src/cs-comparison/resources/cs/integration/schemas/gist_stomach.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/gist_stomach.gz
rename to src/cs-comparison/resources/cs/integration/schemas/gist_stomach.gz
diff --git a/src/test/resources/cs/integration/schemas/gum_lower.gz b/src/cs-comparison/resources/cs/integration/schemas/gum_lower.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/gum_lower.gz
rename to src/cs-comparison/resources/cs/integration/schemas/gum_lower.gz
diff --git a/src/test/resources/cs/integration/schemas/gum_other.gz b/src/cs-comparison/resources/cs/integration/schemas/gum_other.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/gum_other.gz
rename to src/cs-comparison/resources/cs/integration/schemas/gum_other.gz
diff --git a/src/test/resources/cs/integration/schemas/gum_upper.gz b/src/cs-comparison/resources/cs/integration/schemas/gum_upper.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/gum_upper.gz
rename to src/cs-comparison/resources/cs/integration/schemas/gum_upper.gz
diff --git a/src/test/resources/cs/integration/schemas/heart_mediastinum.gz b/src/cs-comparison/resources/cs/integration/schemas/heart_mediastinum.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/heart_mediastinum.gz
rename to src/cs-comparison/resources/cs/integration/schemas/heart_mediastinum.gz
diff --git a/src/test/resources/cs/integration/schemas/hypopharynx.gz b/src/cs-comparison/resources/cs/integration/schemas/hypopharynx.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/hypopharynx.gz
rename to src/cs-comparison/resources/cs/integration/schemas/hypopharynx.gz
diff --git a/src/test/resources/cs/integration/schemas/ill_defined_other.gz b/src/cs-comparison/resources/cs/integration/schemas/ill_defined_other.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/ill_defined_other.gz
rename to src/cs-comparison/resources/cs/integration/schemas/ill_defined_other.gz
diff --git a/src/test/resources/cs/integration/schemas/index.txt b/src/cs-comparison/resources/cs/integration/schemas/index.txt
similarity index 100%
rename from src/test/resources/cs/integration/schemas/index.txt
rename to src/cs-comparison/resources/cs/integration/schemas/index.txt
diff --git a/src/test/resources/cs/integration/schemas/intracranial_gland.gz b/src/cs-comparison/resources/cs/integration/schemas/intracranial_gland.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/intracranial_gland.gz
rename to src/cs-comparison/resources/cs/integration/schemas/intracranial_gland.gz
diff --git a/src/test/resources/cs/integration/schemas/kaposi_sarcoma.gz b/src/cs-comparison/resources/cs/integration/schemas/kaposi_sarcoma.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/kaposi_sarcoma.gz
rename to src/cs-comparison/resources/cs/integration/schemas/kaposi_sarcoma.gz
diff --git a/src/test/resources/cs/integration/schemas/kidney_parenchyma.gz b/src/cs-comparison/resources/cs/integration/schemas/kidney_parenchyma.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/kidney_parenchyma.gz
rename to src/cs-comparison/resources/cs/integration/schemas/kidney_parenchyma.gz
diff --git a/src/test/resources/cs/integration/schemas/kidney_renal_pelvis.gz b/src/cs-comparison/resources/cs/integration/schemas/kidney_renal_pelvis.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/kidney_renal_pelvis.gz
rename to src/cs-comparison/resources/cs/integration/schemas/kidney_renal_pelvis.gz
diff --git a/src/test/resources/cs/integration/schemas/lacrimal_gland.gz b/src/cs-comparison/resources/cs/integration/schemas/lacrimal_gland.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/lacrimal_gland.gz
rename to src/cs-comparison/resources/cs/integration/schemas/lacrimal_gland.gz
diff --git a/src/test/resources/cs/integration/schemas/lacrimal_sac.gz b/src/cs-comparison/resources/cs/integration/schemas/lacrimal_sac.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/lacrimal_sac.gz
rename to src/cs-comparison/resources/cs/integration/schemas/lacrimal_sac.gz
diff --git a/src/test/resources/cs/integration/schemas/larynx_glottic.gz b/src/cs-comparison/resources/cs/integration/schemas/larynx_glottic.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/larynx_glottic.gz
rename to src/cs-comparison/resources/cs/integration/schemas/larynx_glottic.gz
diff --git a/src/test/resources/cs/integration/schemas/larynx_other.gz b/src/cs-comparison/resources/cs/integration/schemas/larynx_other.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/larynx_other.gz
rename to src/cs-comparison/resources/cs/integration/schemas/larynx_other.gz
diff --git a/src/test/resources/cs/integration/schemas/larynx_subglottic.gz b/src/cs-comparison/resources/cs/integration/schemas/larynx_subglottic.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/larynx_subglottic.gz
rename to src/cs-comparison/resources/cs/integration/schemas/larynx_subglottic.gz
diff --git a/src/test/resources/cs/integration/schemas/larynx_supraglottic.gz b/src/cs-comparison/resources/cs/integration/schemas/larynx_supraglottic.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/larynx_supraglottic.gz
rename to src/cs-comparison/resources/cs/integration/schemas/larynx_supraglottic.gz
diff --git a/src/test/resources/cs/integration/schemas/lip_lower.gz b/src/cs-comparison/resources/cs/integration/schemas/lip_lower.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/lip_lower.gz
rename to src/cs-comparison/resources/cs/integration/schemas/lip_lower.gz
diff --git a/src/test/resources/cs/integration/schemas/lip_other.gz b/src/cs-comparison/resources/cs/integration/schemas/lip_other.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/lip_other.gz
rename to src/cs-comparison/resources/cs/integration/schemas/lip_other.gz
diff --git a/src/test/resources/cs/integration/schemas/lip_upper.gz b/src/cs-comparison/resources/cs/integration/schemas/lip_upper.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/lip_upper.gz
rename to src/cs-comparison/resources/cs/integration/schemas/lip_upper.gz
diff --git a/src/test/resources/cs/integration/schemas/liver.gz b/src/cs-comparison/resources/cs/integration/schemas/liver.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/liver.gz
rename to src/cs-comparison/resources/cs/integration/schemas/liver.gz
diff --git a/src/test/resources/cs/integration/schemas/melanoma_buccal_mucosa.gz b/src/cs-comparison/resources/cs/integration/schemas/melanoma_buccal_mucosa.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/melanoma_buccal_mucosa.gz
rename to src/cs-comparison/resources/cs/integration/schemas/melanoma_buccal_mucosa.gz
diff --git a/src/test/resources/cs/integration/schemas/melanoma_epiglottis_anterior.gz b/src/cs-comparison/resources/cs/integration/schemas/melanoma_epiglottis_anterior.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/melanoma_epiglottis_anterior.gz
rename to src/cs-comparison/resources/cs/integration/schemas/melanoma_epiglottis_anterior.gz
diff --git a/src/test/resources/cs/integration/schemas/melanoma_eye_other.gz b/src/cs-comparison/resources/cs/integration/schemas/melanoma_eye_other.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/melanoma_eye_other.gz
rename to src/cs-comparison/resources/cs/integration/schemas/melanoma_eye_other.gz
diff --git a/src/test/resources/cs/integration/schemas/melanoma_floor_mouth.gz b/src/cs-comparison/resources/cs/integration/schemas/melanoma_floor_mouth.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/melanoma_floor_mouth.gz
rename to src/cs-comparison/resources/cs/integration/schemas/melanoma_floor_mouth.gz
diff --git a/src/test/resources/cs/integration/schemas/melanoma_gum_lower.gz b/src/cs-comparison/resources/cs/integration/schemas/melanoma_gum_lower.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/melanoma_gum_lower.gz
rename to src/cs-comparison/resources/cs/integration/schemas/melanoma_gum_lower.gz
diff --git a/src/test/resources/cs/integration/schemas/melanoma_gum_other.gz b/src/cs-comparison/resources/cs/integration/schemas/melanoma_gum_other.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/melanoma_gum_other.gz
rename to src/cs-comparison/resources/cs/integration/schemas/melanoma_gum_other.gz
diff --git a/src/test/resources/cs/integration/schemas/melanoma_gum_upper.gz b/src/cs-comparison/resources/cs/integration/schemas/melanoma_gum_upper.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/melanoma_gum_upper.gz
rename to src/cs-comparison/resources/cs/integration/schemas/melanoma_gum_upper.gz
diff --git a/src/test/resources/cs/integration/schemas/melanoma_hypopharynx.gz b/src/cs-comparison/resources/cs/integration/schemas/melanoma_hypopharynx.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/melanoma_hypopharynx.gz
rename to src/cs-comparison/resources/cs/integration/schemas/melanoma_hypopharynx.gz
diff --git a/src/test/resources/cs/integration/schemas/melanoma_iris.gz b/src/cs-comparison/resources/cs/integration/schemas/melanoma_iris.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/melanoma_iris.gz
rename to src/cs-comparison/resources/cs/integration/schemas/melanoma_iris.gz
diff --git a/src/test/resources/cs/integration/schemas/melanoma_larynx_glottic.gz b/src/cs-comparison/resources/cs/integration/schemas/melanoma_larynx_glottic.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/melanoma_larynx_glottic.gz
rename to src/cs-comparison/resources/cs/integration/schemas/melanoma_larynx_glottic.gz
diff --git a/src/test/resources/cs/integration/schemas/melanoma_larynx_other.gz b/src/cs-comparison/resources/cs/integration/schemas/melanoma_larynx_other.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/melanoma_larynx_other.gz
rename to src/cs-comparison/resources/cs/integration/schemas/melanoma_larynx_other.gz
diff --git a/src/test/resources/cs/integration/schemas/melanoma_larynx_subglottic.gz b/src/cs-comparison/resources/cs/integration/schemas/melanoma_larynx_subglottic.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/melanoma_larynx_subglottic.gz
rename to src/cs-comparison/resources/cs/integration/schemas/melanoma_larynx_subglottic.gz
diff --git a/src/test/resources/cs/integration/schemas/melanoma_larynx_supraglottic.gz b/src/cs-comparison/resources/cs/integration/schemas/melanoma_larynx_supraglottic.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/melanoma_larynx_supraglottic.gz
rename to src/cs-comparison/resources/cs/integration/schemas/melanoma_larynx_supraglottic.gz
diff --git a/src/test/resources/cs/integration/schemas/melanoma_lip_lower.gz b/src/cs-comparison/resources/cs/integration/schemas/melanoma_lip_lower.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/melanoma_lip_lower.gz
rename to src/cs-comparison/resources/cs/integration/schemas/melanoma_lip_lower.gz
diff --git a/src/test/resources/cs/integration/schemas/melanoma_lip_other.gz b/src/cs-comparison/resources/cs/integration/schemas/melanoma_lip_other.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/melanoma_lip_other.gz
rename to src/cs-comparison/resources/cs/integration/schemas/melanoma_lip_other.gz
diff --git a/src/test/resources/cs/integration/schemas/melanoma_lip_upper.gz b/src/cs-comparison/resources/cs/integration/schemas/melanoma_lip_upper.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/melanoma_lip_upper.gz
rename to src/cs-comparison/resources/cs/integration/schemas/melanoma_lip_upper.gz
diff --git a/src/test/resources/cs/integration/schemas/melanoma_mouth_other.gz b/src/cs-comparison/resources/cs/integration/schemas/melanoma_mouth_other.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/melanoma_mouth_other.gz
rename to src/cs-comparison/resources/cs/integration/schemas/melanoma_mouth_other.gz
diff --git a/src/test/resources/cs/integration/schemas/melanoma_nasal_cavity.gz b/src/cs-comparison/resources/cs/integration/schemas/melanoma_nasal_cavity.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/melanoma_nasal_cavity.gz
rename to src/cs-comparison/resources/cs/integration/schemas/melanoma_nasal_cavity.gz
diff --git a/src/test/resources/cs/integration/schemas/melanoma_nasopharynx.gz b/src/cs-comparison/resources/cs/integration/schemas/melanoma_nasopharynx.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/melanoma_nasopharynx.gz
rename to src/cs-comparison/resources/cs/integration/schemas/melanoma_nasopharynx.gz
diff --git a/src/test/resources/cs/integration/schemas/melanoma_oropharynx.gz b/src/cs-comparison/resources/cs/integration/schemas/melanoma_oropharynx.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/melanoma_oropharynx.gz
rename to src/cs-comparison/resources/cs/integration/schemas/melanoma_oropharynx.gz
diff --git a/src/test/resources/cs/integration/schemas/melanoma_palate_hard.gz b/src/cs-comparison/resources/cs/integration/schemas/melanoma_palate_hard.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/melanoma_palate_hard.gz
rename to src/cs-comparison/resources/cs/integration/schemas/melanoma_palate_hard.gz
diff --git a/src/test/resources/cs/integration/schemas/melanoma_palate_soft.gz b/src/cs-comparison/resources/cs/integration/schemas/melanoma_palate_soft.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/melanoma_palate_soft.gz
rename to src/cs-comparison/resources/cs/integration/schemas/melanoma_palate_soft.gz
diff --git a/src/test/resources/cs/integration/schemas/melanoma_pharynx_other.gz b/src/cs-comparison/resources/cs/integration/schemas/melanoma_pharynx_other.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/melanoma_pharynx_other.gz
rename to src/cs-comparison/resources/cs/integration/schemas/melanoma_pharynx_other.gz
diff --git a/src/test/resources/cs/integration/schemas/melanoma_sinus_ethmoid.gz b/src/cs-comparison/resources/cs/integration/schemas/melanoma_sinus_ethmoid.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/melanoma_sinus_ethmoid.gz
rename to src/cs-comparison/resources/cs/integration/schemas/melanoma_sinus_ethmoid.gz
diff --git a/src/test/resources/cs/integration/schemas/melanoma_sinus_maxillary.gz b/src/cs-comparison/resources/cs/integration/schemas/melanoma_sinus_maxillary.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/melanoma_sinus_maxillary.gz
rename to src/cs-comparison/resources/cs/integration/schemas/melanoma_sinus_maxillary.gz
diff --git a/src/test/resources/cs/integration/schemas/melanoma_sinus_other.gz b/src/cs-comparison/resources/cs/integration/schemas/melanoma_sinus_other.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/melanoma_sinus_other.gz
rename to src/cs-comparison/resources/cs/integration/schemas/melanoma_sinus_other.gz
diff --git a/src/test/resources/cs/integration/schemas/melanoma_tongue_anterior.gz b/src/cs-comparison/resources/cs/integration/schemas/melanoma_tongue_anterior.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/melanoma_tongue_anterior.gz
rename to src/cs-comparison/resources/cs/integration/schemas/melanoma_tongue_anterior.gz
diff --git a/src/test/resources/cs/integration/schemas/melanoma_tongue_base.gz b/src/cs-comparison/resources/cs/integration/schemas/melanoma_tongue_base.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/melanoma_tongue_base.gz
rename to src/cs-comparison/resources/cs/integration/schemas/melanoma_tongue_base.gz
diff --git a/src/test/resources/cs/integration/schemas/middle_ear.gz b/src/cs-comparison/resources/cs/integration/schemas/middle_ear.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/middle_ear.gz
rename to src/cs-comparison/resources/cs/integration/schemas/middle_ear.gz
diff --git a/src/test/resources/cs/integration/schemas/mouth_other.gz b/src/cs-comparison/resources/cs/integration/schemas/mouth_other.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/mouth_other.gz
rename to src/cs-comparison/resources/cs/integration/schemas/mouth_other.gz
diff --git a/src/test/resources/cs/integration/schemas/mycosis_fungoides.gz b/src/cs-comparison/resources/cs/integration/schemas/mycosis_fungoides.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/mycosis_fungoides.gz
rename to src/cs-comparison/resources/cs/integration/schemas/mycosis_fungoides.gz
diff --git a/src/test/resources/cs/integration/schemas/myeloma_plasma_cell_disorder.gz b/src/cs-comparison/resources/cs/integration/schemas/myeloma_plasma_cell_disorder.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/myeloma_plasma_cell_disorder.gz
rename to src/cs-comparison/resources/cs/integration/schemas/myeloma_plasma_cell_disorder.gz
diff --git a/src/test/resources/cs/integration/schemas/nasal_cavity.gz b/src/cs-comparison/resources/cs/integration/schemas/nasal_cavity.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/nasal_cavity.gz
rename to src/cs-comparison/resources/cs/integration/schemas/nasal_cavity.gz
diff --git a/src/test/resources/cs/integration/schemas/nasopharynx.gz b/src/cs-comparison/resources/cs/integration/schemas/nasopharynx.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/nasopharynx.gz
rename to src/cs-comparison/resources/cs/integration/schemas/nasopharynx.gz
diff --git a/src/test/resources/cs/integration/schemas/net_ampulla.gz b/src/cs-comparison/resources/cs/integration/schemas/net_ampulla.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/net_ampulla.gz
rename to src/cs-comparison/resources/cs/integration/schemas/net_ampulla.gz
diff --git a/src/test/resources/cs/integration/schemas/net_small_intestine.gz b/src/cs-comparison/resources/cs/integration/schemas/net_small_intestine.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/net_small_intestine.gz
rename to src/cs-comparison/resources/cs/integration/schemas/net_small_intestine.gz
diff --git a/src/test/resources/cs/integration/schemas/orbit.gz b/src/cs-comparison/resources/cs/integration/schemas/orbit.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/orbit.gz
rename to src/cs-comparison/resources/cs/integration/schemas/orbit.gz
diff --git a/src/test/resources/cs/integration/schemas/ovary.gz b/src/cs-comparison/resources/cs/integration/schemas/ovary.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/ovary.gz
rename to src/cs-comparison/resources/cs/integration/schemas/ovary.gz
diff --git a/src/test/resources/cs/integration/schemas/palate_hard.gz b/src/cs-comparison/resources/cs/integration/schemas/palate_hard.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/palate_hard.gz
rename to src/cs-comparison/resources/cs/integration/schemas/palate_hard.gz
diff --git a/src/test/resources/cs/integration/schemas/palate_soft.gz b/src/cs-comparison/resources/cs/integration/schemas/palate_soft.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/palate_soft.gz
rename to src/cs-comparison/resources/cs/integration/schemas/palate_soft.gz
diff --git a/src/test/resources/cs/integration/schemas/pancreas_body_tail.gz b/src/cs-comparison/resources/cs/integration/schemas/pancreas_body_tail.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/pancreas_body_tail.gz
rename to src/cs-comparison/resources/cs/integration/schemas/pancreas_body_tail.gz
diff --git a/src/test/resources/cs/integration/schemas/pancreas_head.gz b/src/cs-comparison/resources/cs/integration/schemas/pancreas_head.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/pancreas_head.gz
rename to src/cs-comparison/resources/cs/integration/schemas/pancreas_head.gz
diff --git a/src/test/resources/cs/integration/schemas/pancreas_other.gz b/src/cs-comparison/resources/cs/integration/schemas/pancreas_other.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/pancreas_other.gz
rename to src/cs-comparison/resources/cs/integration/schemas/pancreas_other.gz
diff --git a/src/test/resources/cs/integration/schemas/parotid_gland.gz b/src/cs-comparison/resources/cs/integration/schemas/parotid_gland.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/parotid_gland.gz
rename to src/cs-comparison/resources/cs/integration/schemas/parotid_gland.gz
diff --git a/src/test/resources/cs/integration/schemas/penis.gz b/src/cs-comparison/resources/cs/integration/schemas/penis.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/penis.gz
rename to src/cs-comparison/resources/cs/integration/schemas/penis.gz
diff --git a/src/test/resources/cs/integration/schemas/peritoneum.gz b/src/cs-comparison/resources/cs/integration/schemas/peritoneum.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/peritoneum.gz
rename to src/cs-comparison/resources/cs/integration/schemas/peritoneum.gz
diff --git a/src/test/resources/cs/integration/schemas/pharyngeal_tonsil.gz b/src/cs-comparison/resources/cs/integration/schemas/pharyngeal_tonsil.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/pharyngeal_tonsil.gz
rename to src/cs-comparison/resources/cs/integration/schemas/pharyngeal_tonsil.gz
diff --git a/src/test/resources/cs/integration/schemas/pharynx_other.gz b/src/cs-comparison/resources/cs/integration/schemas/pharynx_other.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/pharynx_other.gz
rename to src/cs-comparison/resources/cs/integration/schemas/pharynx_other.gz
diff --git a/src/test/resources/cs/integration/schemas/placenta.gz b/src/cs-comparison/resources/cs/integration/schemas/placenta.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/placenta.gz
rename to src/cs-comparison/resources/cs/integration/schemas/placenta.gz
diff --git a/src/test/resources/cs/integration/schemas/respiratory_other.gz b/src/cs-comparison/resources/cs/integration/schemas/respiratory_other.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/respiratory_other.gz
rename to src/cs-comparison/resources/cs/integration/schemas/respiratory_other.gz
diff --git a/src/test/resources/cs/integration/schemas/retroperitoneum.gz b/src/cs-comparison/resources/cs/integration/schemas/retroperitoneum.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/retroperitoneum.gz
rename to src/cs-comparison/resources/cs/integration/schemas/retroperitoneum.gz
diff --git a/src/test/resources/cs/integration/schemas/salivary_gland_other.gz b/src/cs-comparison/resources/cs/integration/schemas/salivary_gland_other.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/salivary_gland_other.gz
rename to src/cs-comparison/resources/cs/integration/schemas/salivary_gland_other.gz
diff --git a/src/test/resources/cs/integration/schemas/sinus_ethmoid.gz b/src/cs-comparison/resources/cs/integration/schemas/sinus_ethmoid.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/sinus_ethmoid.gz
rename to src/cs-comparison/resources/cs/integration/schemas/sinus_ethmoid.gz
diff --git a/src/test/resources/cs/integration/schemas/sinus_maxillary.gz b/src/cs-comparison/resources/cs/integration/schemas/sinus_maxillary.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/sinus_maxillary.gz
rename to src/cs-comparison/resources/cs/integration/schemas/sinus_maxillary.gz
diff --git a/src/test/resources/cs/integration/schemas/sinus_other.gz b/src/cs-comparison/resources/cs/integration/schemas/sinus_other.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/sinus_other.gz
rename to src/cs-comparison/resources/cs/integration/schemas/sinus_other.gz
diff --git a/src/test/resources/cs/integration/schemas/submandibular_gland.gz b/src/cs-comparison/resources/cs/integration/schemas/submandibular_gland.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/submandibular_gland.gz
rename to src/cs-comparison/resources/cs/integration/schemas/submandibular_gland.gz
diff --git a/src/test/resources/cs/integration/schemas/tongue_anterior.gz b/src/cs-comparison/resources/cs/integration/schemas/tongue_anterior.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/tongue_anterior.gz
rename to src/cs-comparison/resources/cs/integration/schemas/tongue_anterior.gz
diff --git a/src/test/resources/cs/integration/schemas/tongue_base.gz b/src/cs-comparison/resources/cs/integration/schemas/tongue_base.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/tongue_base.gz
rename to src/cs-comparison/resources/cs/integration/schemas/tongue_base.gz
diff --git a/src/test/resources/cs/integration/schemas/trachea.gz b/src/cs-comparison/resources/cs/integration/schemas/trachea.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/trachea.gz
rename to src/cs-comparison/resources/cs/integration/schemas/trachea.gz
diff --git a/src/test/resources/cs/integration/schemas/urethra.gz b/src/cs-comparison/resources/cs/integration/schemas/urethra.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/urethra.gz
rename to src/cs-comparison/resources/cs/integration/schemas/urethra.gz
diff --git a/src/test/resources/cs/integration/schemas/urinary_other.gz b/src/cs-comparison/resources/cs/integration/schemas/urinary_other.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/urinary_other.gz
rename to src/cs-comparison/resources/cs/integration/schemas/urinary_other.gz
diff --git a/src/test/resources/cs/integration/schemas/vagina.gz b/src/cs-comparison/resources/cs/integration/schemas/vagina.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/vagina.gz
rename to src/cs-comparison/resources/cs/integration/schemas/vagina.gz
diff --git a/src/test/resources/cs/integration/schemas/vulva.gz b/src/cs-comparison/resources/cs/integration/schemas/vulva.gz
similarity index 100%
rename from src/test/resources/cs/integration/schemas/vulva.gz
rename to src/cs-comparison/resources/cs/integration/schemas/vulva.gz
diff --git a/src/test/java/com/imsweb/staging/AlgorithmStagingDataTest.java b/src/test/java/com/imsweb/staging/AlgorithmStagingDataTest.java
new file mode 100644
index 000000000..2851bd4c6
--- /dev/null
+++ b/src/test/java/com/imsweb/staging/AlgorithmStagingDataTest.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2026 Information Management Services, Inc.
+ */
+package com.imsweb.staging;
+
+import java.util.function.BiConsumer;
+import java.util.function.Function;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import com.imsweb.staging.cs.CsStagingData;
+import com.imsweb.staging.cs.CsStagingData.CsInput;
+import com.imsweb.staging.cs.CsStagingData.CsOutput;
+import com.imsweb.staging.cs.CsStagingData.CsStagingInputBuilder;
+import com.imsweb.staging.eod.EodStagingData;
+import com.imsweb.staging.eod.EodStagingData.EodInput;
+import com.imsweb.staging.eod.EodStagingData.EodOutput;
+import com.imsweb.staging.eod.EodStagingData.EodStagingInputBuilder;
+import com.imsweb.staging.tnm.TnmStagingData;
+import com.imsweb.staging.tnm.TnmStagingData.TnmInput;
+import com.imsweb.staging.tnm.TnmStagingData.TnmOutput;
+import com.imsweb.staging.tnm.TnmStagingData.TnmStagingInputBuilder;
+
+import static org.junit.jupiter.api.Assertions.assertAll;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class AlgorithmStagingDataTest {
+
+ private static final String SITE = "C509";
+ private static final String HISTOLOGY = "8000";
+
+ @Test
+ void testCsConstructors() {
+ CsStagingData empty = new CsStagingData();
+ CsStagingData siteAndHistology = new CsStagingData(SITE, HISTOLOGY);
+ CsStagingData withSsf25 = new CsStagingData(SITE, HISTOLOGY, "025");
+
+ assertAll(
+ () -> assertTrue(empty.getInput().isEmpty()),
+ () -> assertSiteAndHistology(siteAndHistology),
+ () -> assertSiteAndHistology(withSsf25),
+ () -> assertEquals("025", withSsf25.getSsf(25)));
+ }
+
+ @Test
+ void testTnmConstructors() {
+ TnmStagingData empty = new TnmStagingData();
+ TnmStagingData siteAndHistology = new TnmStagingData(SITE, HISTOLOGY);
+ TnmStagingData withSsf25 = new TnmStagingData(SITE, HISTOLOGY, "025");
+
+ assertAll(
+ () -> assertTrue(empty.getInput().isEmpty()),
+ () -> assertSiteAndHistology(siteAndHistology),
+ () -> assertSiteAndHistology(withSsf25),
+ () -> assertEquals("025", withSsf25.getSsf(25)));
+ }
+
+ @Test
+ void testEodConstructors() {
+ EodStagingData empty = new EodStagingData();
+ EodStagingData siteAndHistology = new EodStagingData(SITE, HISTOLOGY);
+ EodStagingData withFirstDiscriminator = new EodStagingData(SITE, HISTOLOGY, "A");
+ EodStagingData withBothDiscriminators = new EodStagingData(SITE, HISTOLOGY, "A", "B");
+
+ assertAll(
+ () -> assertTrue(empty.getInput().isEmpty()),
+ () -> assertSiteAndHistology(siteAndHistology),
+ () -> assertSiteAndHistology(withFirstDiscriminator),
+ () -> assertEquals("A", withFirstDiscriminator.getInput(EodInput.DISCRIMINATOR_1)),
+ () -> assertSiteAndHistology(withBothDiscriminators),
+ () -> assertEquals("A", withBothDiscriminators.getInput(EodInput.DISCRIMINATOR_1)),
+ () -> assertEquals("B", withBothDiscriminators.getInput(EodInput.DISCRIMINATOR_2)));
+ }
+
+ @ParameterizedTest
+ @EnumSource(CsInput.class)
+ void testCsTypedInputAccess(CsInput key) {
+ CsStagingData data = new CsStagingData();
+
+ data.setInput(key, "value");
+
+ assertAll(
+ () -> assertEquals("value", data.getInput(key)),
+ () -> assertEquals("value", data.getInput(key.toString())));
+ }
+
+ @ParameterizedTest
+ @EnumSource(TnmInput.class)
+ void testTnmTypedInputAccess(TnmInput key) {
+ TnmStagingData data = new TnmStagingData();
+
+ data.setInput(key, "value");
+
+ assertAll(
+ () -> assertEquals("value", data.getInput(key)),
+ () -> assertEquals("value", data.getInput(key.toString())));
+ }
+
+ @ParameterizedTest
+ @EnumSource(EodInput.class)
+ void testEodTypedInputAccess(EodInput key) {
+ EodStagingData data = new EodStagingData();
+
+ data.setInput(key, "value");
+
+ assertAll(
+ () -> assertEquals("value", data.getInput(key)),
+ () -> assertEquals("value", data.getInput(key.toString())));
+ }
+
+ @ParameterizedTest
+ @EnumSource(CsOutput.class)
+ void testCsTypedOutputAccess(CsOutput key) {
+ CsStagingData data = new CsStagingData();
+ data.getOutput().put(key.toString(), "value");
+
+ assertEquals("value", data.getOutput(key));
+ }
+
+ @ParameterizedTest
+ @EnumSource(TnmOutput.class)
+ void testTnmTypedOutputAccess(TnmOutput key) {
+ TnmStagingData data = new TnmStagingData();
+ data.getOutput().put(key.toString(), "value");
+
+ assertEquals("value", data.getOutput(key));
+ }
+
+ @ParameterizedTest
+ @EnumSource(EodOutput.class)
+ void testEodTypedOutputAccess(EodOutput key) {
+ EodStagingData data = new EodStagingData();
+ data.getOutput().put(key.toString(), "value");
+
+ assertEquals("value", data.getOutput(key));
+ }
+
+ @ParameterizedTest
+ @ValueSource(ints = {1, 25})
+ void testCsSsfBoundaries(int index) {
+ CsStagingData data = new CsStagingData();
+
+ assertValidSsf(index, data::setSsf, data::getSsf);
+ }
+
+ @ParameterizedTest
+ @ValueSource(ints = {1, 25})
+ void testTnmSsfBoundaries(int index) {
+ TnmStagingData data = new TnmStagingData();
+
+ assertValidSsf(index, data::setSsf, data::getSsf);
+ }
+
+ @ParameterizedTest
+ @ValueSource(ints = {-1, 0, 26, Integer.MAX_VALUE})
+ void testCsRejectsInvalidSsfIndexes(int index) {
+ CsStagingData data = new CsStagingData();
+
+ assertInvalidSsf(index, data::setSsf, data::getSsf);
+ }
+
+ @ParameterizedTest
+ @ValueSource(ints = {-1, 0, 26, Integer.MAX_VALUE})
+ void testTnmRejectsInvalidSsfIndexes(int index) {
+ TnmStagingData data = new TnmStagingData();
+
+ assertInvalidSsf(index, data::setSsf, data::getSsf);
+ }
+
+ @Test
+ void testCsBuilder() {
+ CsStagingData data = new CsStagingInputBuilder()
+ .withInput(CsInput.BEHAVIOR, "3")
+ .withSsf(1, "001")
+ .withSsf(25, "025")
+ .build();
+
+ assertAll(
+ () -> assertEquals("3", data.getInput(CsInput.BEHAVIOR)),
+ () -> assertEquals("001", data.getSsf(1)),
+ () -> assertEquals("025", data.getSsf(25)));
+ }
+
+ @Test
+ void testTnmBuilder() {
+ TnmStagingData data = new TnmStagingInputBuilder()
+ .withInput(TnmInput.BEHAVIOR, "3")
+ .withSsf(1, "001")
+ .withSsf(25, "025")
+ .build();
+
+ assertAll(
+ () -> assertEquals("3", data.getInput(TnmInput.BEHAVIOR)),
+ () -> assertEquals("001", data.getSsf(1)),
+ () -> assertEquals("025", data.getSsf(25)));
+ }
+
+ @Test
+ void testEodDiscriminatorBuilder() {
+ EodStagingData data = new EodStagingInputBuilder()
+ .withDisciminator1("A")
+ .withDisciminator2("B")
+ .withInput(EodInput.BEHAVIOR, "3")
+ .build();
+
+ assertAll(
+ () -> assertEquals("A", data.getInput(EodInput.DISCRIMINATOR_1)),
+ () -> assertEquals("B", data.getInput(EodInput.DISCRIMINATOR_2)),
+ () -> assertEquals("3", data.getInput(EodInput.BEHAVIOR)));
+ }
+
+ private static void assertSiteAndHistology(com.imsweb.staging.entities.StagingData data) {
+ assertAll(
+ () -> assertEquals(SITE, data.getInput("site")),
+ () -> assertEquals(HISTOLOGY, data.getInput("hist")));
+ }
+
+ private static void assertValidSsf(int index, BiConsumer setter, Function getter) {
+ setter.accept(index, "value");
+
+ assertEquals("value", getter.apply(index));
+ }
+
+ private static void assertInvalidSsf(int index, BiConsumer setter, Function getter) {
+ assertAll(
+ () -> assertThrows(IllegalStateException.class, () -> setter.accept(index, "value")),
+ () -> assertThrows(IllegalStateException.class, () -> getter.apply(index)));
+ }
+}
diff --git a/src/test/java/com/imsweb/staging/ExternalStagingFileDataProviderTest.java b/src/test/java/com/imsweb/staging/ExternalStagingFileDataProviderTest.java
index b2ee6fcdc..f9efa2140 100644
--- a/src/test/java/com/imsweb/staging/ExternalStagingFileDataProviderTest.java
+++ b/src/test/java/com/imsweb/staging/ExternalStagingFileDataProviderTest.java
@@ -1,23 +1,31 @@
package com.imsweb.staging;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
-
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
-
class ExternalStagingFileDataProviderTest extends FileDataProviderTest {
private static Staging _STAGING;
@BeforeAll
static void setup() throws IOException {
- try (InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("external_algorithm.zip")) {
+ try (
+ InputStream is = Thread.currentThread()
+ .getContextClassLoader()
+ .getResourceAsStream("external_algorithm.zip")
+ ) {
_STAGING = Staging.getInstance(is);
}
}
@@ -62,8 +70,87 @@ void testConstructorWithString() throws IOException {
void testInvalidPathThrowsException() {
Path invalidPath = Paths.get("src/test/resources/missing.zip");
assertThatThrownBy(() -> new ExternalStagingFileDataProvider(invalidPath))
- .isInstanceOf(IOException.class)
- .hasMessageContaining("missing.zip");
+ .isInstanceOf(IOException.class)
+ .hasMessageContaining("missing.zip");
+ }
+
+ @Test
+ void testGlossaryFromInMemoryZip() throws IOException {
+ ExternalStagingFileDataProvider provider = provider(
+ "tables/site.json",
+ tableJson("site", "TESTING", "1.0"),
+ "glossary/cortex.json",
+ "{\"name\":\"Cortex\",\"definition\":\"Outer tissue\",\"alternate_names\":[\"Cortical\"]}"
+ );
+
+ assertThat(provider.getAlgorithm()).isEqualTo("testing");
+ assertThat(provider.getGlossaryTerms()).containsExactly("Cortex");
+ assertThat(provider.getGlossaryDefinition("Cortex").getDefinition()).isEqualTo("Outer tissue");
+ assertThat(provider.getGlossaryDefinition("missing")).isNull();
+ assertThat(provider.getGlossaryMatches("The cortex is present")).extracting("term").containsExactly("Cortex");
+ assertThat(provider.getGlossaryMatches("Cortexlike")).isEmpty();
+ }
+
+ @Test
+ void testMalformedJsonIsRejected() throws IOException {
+ byte[] zip = zip("tables/broken.json", "{not-json");
+
+ assertThatThrownBy(() -> new ExternalStagingFileDataProvider(new ByteArrayInputStream(zip))).isInstanceOf(
+ IOException.class
+ );
}
-}
\ No newline at end of file
+ @Test
+ void testArchiveRequiresAlgorithmData() throws IOException {
+ byte[] zip = zip("notes/readme.txt", "ignored", "glossary/term.json", "{\"name\":\"Term\"}");
+
+ assertThatThrownBy(() -> new ExternalStagingFileDataProvider(new ByteArrayInputStream(zip)))
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessage("Error initializing provider; only a single algorithm should be included in file");
+ }
+
+ @Test
+ void testInconsistentAlgorithmsAndVersionsAreRejected() throws IOException {
+ byte[] algorithms = zip(
+ "tables/one.json",
+ tableJson("one", "FIRST", "1.0"),
+ "tables/two.json",
+ tableJson("two", "SECOND", "1.0")
+ );
+ assertThatThrownBy(() -> new ExternalStagingFileDataProvider(new ByteArrayInputStream(algorithms)))
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessage("Error initializing provider; only a single algorithm should be included in file");
+
+ byte[] versions = zip(
+ "tables/one.json",
+ tableJson("one", "TEST", "1.0"),
+ "tables/two.json",
+ tableJson("two", "TEST", "2.0")
+ );
+ assertThatThrownBy(() -> new ExternalStagingFileDataProvider(new ByteArrayInputStream(versions)))
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessage("Error initializing provider; only a single version should be included in file");
+ }
+
+ private static ExternalStagingFileDataProvider provider(String... entries) throws IOException {
+ return new ExternalStagingFileDataProvider(new ByteArrayInputStream(zip(entries)));
+ }
+
+ private static byte[] zip(String... entries) throws IOException {
+ ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+ try (ZipOutputStream zip = new ZipOutputStream(bytes)) {
+ for (int i = 0; i < entries.length; i += 2) {
+ zip.putNextEntry(new ZipEntry(entries[i]));
+ zip.write(entries[i + 1].getBytes(StandardCharsets.UTF_8));
+ zip.closeEntry();
+ }
+ }
+ return bytes.toByteArray();
+ }
+
+ private static String tableJson(String id, String algorithm, String version) {
+ return """
+ {"id":"%s","algorithm":"%s","version":"%s","definition":[{"key":"value","type":"INPUT"}],"rows":[["1"]]}
+ """.formatted(id, algorithm, version);
+ }
+}
diff --git a/src/test/java/com/imsweb/staging/StagingDataProviderTest.java b/src/test/java/com/imsweb/staging/StagingDataProviderTest.java
index 5fecac409..ea40f3e1c 100644
--- a/src/test/java/com/imsweb/staging/StagingDataProviderTest.java
+++ b/src/test/java/com/imsweb/staging/StagingDataProviderTest.java
@@ -1,23 +1,24 @@
package com.imsweb.staging;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import com.imsweb.staging.entities.ColumnDefinition.ColumnType;
+import com.imsweb.staging.entities.Range;
+import com.imsweb.staging.entities.impl.StagingColumnDefinition;
+import com.imsweb.staging.entities.impl.StagingSchema;
+import com.imsweb.staging.entities.impl.StagingTable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
-
+import java.util.Set;
import org.junit.jupiter.api.Test;
-import com.imsweb.staging.entities.ColumnDefinition.ColumnType;
-import com.imsweb.staging.entities.Range;
-import com.imsweb.staging.entities.impl.StagingColumnDefinition;
-import com.imsweb.staging.entities.impl.StagingTable;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertNull;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
class StagingDataProviderTest {
@Test
@@ -138,7 +139,9 @@ void testSplitValues() {
void testTableRowParsing() {
StagingTable table = new StagingTable();
table.setId("test_table");
- table.setColumnDefinitions(Collections.singletonList(new StagingColumnDefinition("key1", "Input 1", ColumnType.INPUT)));
+ table.setColumnDefinitions(
+ Collections.singletonList(new StagingColumnDefinition("key1", "Input 1", ColumnType.INPUT))
+ );
table.setRawRows(new ArrayList<>());
table.getRawRows().add(Collections.singletonList(",1,2,3"));
table.getRawRows().add(Collections.singletonList("1,2,3,"));
@@ -152,6 +155,107 @@ void testTableRowParsing() {
assertEquals(4, table.getTableRows().get(1).getInputs().get("key1").size());
}
+ @Test
+ void testInMemoryProviderContract() {
+ InMemoryDataProvider provider = new InMemoryDataProvider("Test Algorithm", "1.2.3");
+
+ assertEquals("Test Algorithm", provider.getAlgorithm());
+ assertEquals("1.2.3", provider.getVersion());
+ assertTrue(provider.getTableIds().isEmpty());
+ assertTrue(provider.getSchemaIds().isEmpty());
+ assertNull(provider.getTable("missing"));
+ assertNull(provider.getSchema("missing"));
+
+ StagingSchema schema = new StagingSchema("present");
+ schema.setSchemaSelectionTable("selection");
+ provider.addSchema(schema);
+ assertEquals(Set.of("present"), provider.getSchemaIds());
+ assertEquals(schema, provider.getSchema("present"));
+ }
+
+ @Test
+ void testInMemoryGlossaryIsUnsupported() {
+ InMemoryDataProvider provider = new InMemoryDataProvider("test", "1.0");
+
+ assertGlossaryUnsupported(provider::getGlossaryTerms);
+ assertGlossaryUnsupported(() -> provider.getGlossaryDefinition("term"));
+ assertGlossaryUnsupported(() -> provider.getGlossaryMatches("text"));
+ }
+
+ private static void assertGlossaryUnsupported(Runnable operation) {
+ IllegalStateException exception = assertThrows(IllegalStateException.class, operation::run);
+ assertEquals("Glossary not supported in this provider", exception.getMessage());
+ }
+
+ @Test
+ void testValidValuesForMissingTable() {
+ StagingDataProvider provider = new InMemoryDataProvider("test", "1.0");
+
+ assertEquals(Collections.emptySet(), provider.getValidSites());
+ }
+
+ @Test
+ void testValidValuesRequireExactlyOneInputDefinition() {
+ InMemoryDataProvider noInputs = new InMemoryDataProvider("test", "1.0");
+ noInputs.addTable(
+ table(
+ StagingDataProvider.PRIMARY_SITE_TABLE,
+ new StagingColumnDefinition("result", "Result", ColumnType.ENDPOINT)
+ )
+ );
+
+ RuntimeException noInputException = assertThrows(RuntimeException.class, noInputs::getValidSites);
+ assertInvalidInputDefinition(noInputException);
+
+ InMemoryDataProvider multipleInputs = new InMemoryDataProvider("test", "1.0");
+ multipleInputs.addTable(
+ table(
+ StagingDataProvider.PRIMARY_SITE_TABLE,
+ new StagingColumnDefinition("site", "Site", ColumnType.INPUT),
+ new StagingColumnDefinition("other", "Other", ColumnType.INPUT)
+ )
+ );
+
+ RuntimeException multipleInputException = assertThrows(RuntimeException.class, multipleInputs::getValidSites);
+ assertInvalidInputDefinition(multipleInputException);
+ }
+
+ private static void assertInvalidInputDefinition(RuntimeException exception) {
+ assertTrue(exception.getCause() instanceof IllegalStateException);
+ assertEquals(
+ "Table 'primary_site' must have one and only one INPUT column.",
+ exception.getCause().getMessage()
+ );
+ }
+
+ @Test
+ void testValidValuesExpandRangesWithZeroPadding() {
+ InMemoryDataProvider provider = new InMemoryDataProvider("test", "1.0");
+ StagingTable table = table(
+ StagingDataProvider.PRIMARY_SITE_TABLE,
+ new StagingColumnDefinition("site", "Site", ColumnType.INPUT)
+ );
+ table.setRawRows(
+ Arrays.asList(
+ Collections.singletonList("001"),
+ Collections.singletonList("003-005"),
+ Collections.singletonList("010")
+ )
+ );
+ provider.addTable(table);
+
+ assertEquals(Set.of("001", "003", "004", "005", "010"), provider.getValidSites());
+ assertTrue(provider.isValidSite("004"));
+ assertFalse(provider.isValidSite("4"));
+ }
+
+ private static StagingTable table(String id, StagingColumnDefinition... definitions) {
+ StagingTable table = new StagingTable(id);
+ table.setColumnDefinitions(Arrays.asList(definitions));
+ table.setRawRows(Collections.emptyList());
+ return table;
+ }
+
@Test
void testPadStart() {
assertNull(StagingDataProvider.padStart(null, 1, '0'));
@@ -174,4 +278,4 @@ void testIsNumeric() {
assertFalse(StagingDataProvider.isNumeric("1.1.1"));
assertFalse(StagingDataProvider.isNumeric("NAN"));
}
-}
\ No newline at end of file
+}
diff --git a/src/test/java/com/imsweb/staging/StagingInputsOutputsTest.java b/src/test/java/com/imsweb/staging/StagingInputsOutputsTest.java
new file mode 100644
index 000000000..5aba1cf5f
--- /dev/null
+++ b/src/test/java/com/imsweb/staging/StagingInputsOutputsTest.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2026 Information Management Services, Inc.
+ */
+package com.imsweb.staging;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import com.imsweb.staging.entities.ColumnDefinition.ColumnType;
+import com.imsweb.staging.entities.impl.StagingColumnDefinition;
+import com.imsweb.staging.entities.impl.StagingMapping;
+import com.imsweb.staging.entities.impl.StagingSchema;
+import com.imsweb.staging.entities.impl.StagingSchemaOutput;
+import com.imsweb.staging.entities.impl.StagingTable;
+import com.imsweb.staging.entities.impl.StagingTablePath;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+class StagingInputsOutputsTest {
+
+ private InMemoryDataProvider _provider;
+ private Staging _staging;
+ private StagingTablePath _mainPath;
+ private StagingMapping _mapping;
+
+ @BeforeEach
+ void setUp() {
+ _provider = new InMemoryDataProvider("test", "1.0");
+ _provider.addTable(
+ table(
+ "selection",
+ List.of(
+ new StagingColumnDefinition("selector", "Selector", ColumnType.INPUT),
+ new StagingColumnDefinition(Staging.CTX_YEAR_CURRENT, "Current year", ColumnType.INPUT)
+ ),
+ List.of("*", "*")
+ )
+ );
+ _provider.addTable(
+ table(
+ "inclusion",
+ Collections.singletonList(new StagingColumnDefinition("include_flag", "Include", ColumnType.INPUT)),
+ Collections.singletonList("Y")
+ )
+ );
+ _provider.addTable(
+ table(
+ "exclusion",
+ Collections.singletonList(new StagingColumnDefinition("exclude_flag", "Exclude", ColumnType.INPUT)),
+ Collections.singletonList("Y")
+ )
+ );
+ _provider.addTable(
+ table(
+ "main",
+ List.of(
+ new StagingColumnDefinition("raw_input", "Raw input", ColumnType.INPUT),
+ new StagingColumnDefinition(Staging.CTX_ALGORITHM_VERSION, "Algorithm version", ColumnType.INPUT),
+ new StagingColumnDefinition(Staging.CTX_YEAR_CURRENT, "Current year", ColumnType.INPUT),
+ new StagingColumnDefinition("raw_output", "Raw output", ColumnType.ENDPOINT)
+ ),
+ List.of("*", "*", "*", "VALUE:result")
+ )
+ );
+
+ _mainPath = new StagingTablePath("main");
+ _mainPath.addInputMapping("case_input", "raw_input");
+ _mainPath.addOutputMapping("raw_output", "mapped_output");
+
+ _mapping = new StagingMapping("conditional", Collections.singletonList(_mainPath));
+ _mapping.setInclusionTables(Collections.singletonList(new StagingTablePath("inclusion")));
+ _mapping.setExclusionTables(Collections.singletonList(new StagingTablePath("exclusion")));
+
+ _staging = Staging.getInstance(_provider);
+ }
+
+ @Test
+ void getsMappedTablePathInputsAndOutputs() {
+ assertThat(_staging.getInputs(_mainPath)).containsExactly("case_input");
+ assertThat(_staging.getInputs(_mainPath, new HashSet<>(Set.of("case_input")))).isEmpty();
+ assertThat(_staging.getInputs((StagingTablePath) null)).isEmpty();
+
+ assertThat(_staging.getOutputs(_mainPath)).containsExactly("mapped_output");
+ }
+
+ @Test
+ void getsMappingInputsAndOutputsWithExclusionsAndContext() {
+ Map included = Map.of("include_flag", "Y", "exclude_flag", "N");
+ Map notIncluded = Map.of("include_flag", "N", "exclude_flag", "N");
+ Map excluded = Map.of("include_flag", "Y", "exclude_flag", "Y");
+
+ assertThat(_staging.getInputs(_mapping)).containsExactlyInAnyOrder(
+ "include_flag",
+ "exclude_flag",
+ "case_input"
+ );
+ assertThat(_staging.getInputs(_mapping, included, new HashSet<>())).containsExactlyInAnyOrder(
+ "include_flag",
+ "exclude_flag",
+ "case_input"
+ );
+ assertThat(_staging.getInputs(_mapping, notIncluded, new HashSet<>())).containsExactlyInAnyOrder(
+ "include_flag",
+ "exclude_flag"
+ );
+ assertThat(_staging.getInputs(_mapping, excluded, new HashSet<>(Set.of("include_flag")))).containsExactly(
+ "exclude_flag"
+ );
+
+ assertThat(_staging.getOutputs(_mapping)).containsExactly("mapped_output");
+ assertThat(_staging.getOutputs(_mapping, included)).containsExactly("mapped_output");
+ assertThat(_staging.getOutputs(_mapping, notIncluded)).isEmpty();
+ assertThat(_staging.getOutputs(_mapping, excluded)).isEmpty();
+ }
+
+ @Test
+ void getsSchemaInputsAndExplicitOrInferredOutputs() {
+ Map excluded = Map.of("include_flag", "Y", "exclude_flag", "Y");
+
+ StagingSchema inferred = schema("inferred", _mapping);
+ _provider.addSchema(inferred);
+ inferred.setOutputMap(null);
+
+ assertThat(_staging.getInputs(inferred)).containsExactlyInAnyOrder(
+ "selector",
+ "include_flag",
+ "exclude_flag",
+ "case_input"
+ );
+ assertThat(_staging.getInputs(inferred, excluded)).containsExactlyInAnyOrder(
+ "selector",
+ "include_flag",
+ "exclude_flag"
+ );
+ assertThat(_staging.getOutputs(inferred)).containsExactly("mapped_output");
+ assertThat(_staging.getOutputs(inferred, excluded)).isEmpty();
+
+ StagingSchema explicit = schema("explicit", _mapping);
+ explicit.setOutputs(
+ Arrays.asList(
+ new StagingSchemaOutput("declared_one", "Declared one"),
+ new StagingSchemaOutput("declared_two", "Declared two")
+ )
+ );
+ _provider.addSchema(explicit);
+
+ assertThat(_staging.getOutputs(explicit)).containsExactlyInAnyOrder("declared_one", "declared_two");
+ assertThat(_staging.getOutputs(explicit, excluded)).containsExactlyInAnyOrder("declared_one", "declared_two");
+ }
+
+ private StagingSchema schema(String id, StagingMapping mapping) {
+ StagingSchema schema = new StagingSchema(id);
+ schema.setSchemaSelectionTable("selection");
+ schema.setMappings(Collections.singletonList(mapping));
+ return schema;
+ }
+
+ private StagingTable table(String id, List definitions, List row) {
+ StagingTable table = new StagingTable(id);
+ table.setColumnDefinitions(definitions);
+ table.setRawRows(Collections.singletonList(row));
+ return table;
+ }
+}
diff --git a/src/test/java/com/imsweb/staging/engine/DecisionEngineTest.java b/src/test/java/com/imsweb/staging/engine/DecisionEngineTest.java
index dbb270861..64506300b 100644
--- a/src/test/java/com/imsweb/staging/engine/DecisionEngineTest.java
+++ b/src/test/java/com/imsweb/staging/engine/DecisionEngineTest.java
@@ -4,18 +4,15 @@
*/
package com.imsweb.staging.engine;
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.Test;
+import static com.imsweb.staging.engine.DecisionEngine.BLANK_OUTPUT;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.tuple;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import com.imsweb.staging.InMemoryDataProvider;
import com.imsweb.staging.entities.ColumnDefinition.ColumnType;
@@ -38,16 +35,17 @@
import com.imsweb.staging.entities.impl.StagingSchemaOutput;
import com.imsweb.staging.entities.impl.StagingTable;
import com.imsweb.staging.entities.impl.StagingTablePath;
-
-import static com.imsweb.staging.engine.DecisionEngine.BLANK_OUTPUT;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.tuple;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertNull;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
/**
* Test class for DecisionEngine
@@ -242,7 +240,9 @@ static void init() {
schema.addInput("c");
schema.addInitialContext("d", "HARD-CODE");
StagingMapping mapping = new StagingMapping("m1");
- mapping.setTablePaths(Arrays.asList(new StagingTablePath("table_sample_first"), new StagingTablePath("table_sample_second")));
+ mapping.setTablePaths(
+ Arrays.asList(new StagingTablePath("table_sample_first"), new StagingTablePath("table_sample_second"))
+ );
schema.addMapping(mapping);
provider.addSchema(schema);
@@ -256,7 +256,9 @@ static void init() {
schema.addInput("a");
schema.addInput("b");
schema.addInput("c");
- schema.addMapping(new StagingMapping("m1", Collections.singletonList(new StagingTablePath("table_multiple_inputs"))));
+ schema.addMapping(
+ new StagingMapping("m1", Collections.singletonList(new StagingTablePath("table_multiple_inputs")))
+ );
provider.addSchema(schema);
schema = new StagingSchema("starting_recursion");
@@ -404,8 +406,8 @@ void testMatchTable() {
input.put("size", "003");
assertThat(DecisionEngine.matchTable(matchTable, input))
- .extracting(Endpoint::getType, Endpoint::getValue, Endpoint::getResultKey)
- .containsExactly(tuple(EndpointType.JUMP, "some_crazy_table", "size_result"));
+ .extracting(Endpoint::getType, Endpoint::getValue, Endpoint::getResultKey)
+ .containsExactly(tuple(EndpointType.JUMP, "some_crazy_table", "size_result"));
input.put("size", "014");
List extends Endpoint> results = DecisionEngine.matchTable(matchTable, input);
@@ -416,8 +418,8 @@ void testMatchTable() {
input.put("size", "086");
assertThat(DecisionEngine.matchTable(matchTable, input))
- .extracting(Endpoint::getType, Endpoint::getValue, Endpoint::getResultKey)
- .containsExactly(tuple(EndpointType.ERROR, "Get that huge stuff out of here!", "size_result"));
+ .extracting(Endpoint::getType, Endpoint::getValue, Endpoint::getResultKey)
+ .containsExactly(tuple(EndpointType.ERROR, "Get that huge stuff out of here!", "size_result"));
// try with a value not in the table
input.put("size", "021");
@@ -497,30 +499,30 @@ void testMatchOnSpecificKeys() {
// specify to only match on key1, there should be a match to the first line
assertThat(DecisionEngine.matchTable(matchTable, input, new HashSet<>(Collections.singletonList("key1"))))
- .extracting(Endpoint::getType, Endpoint::getValue, Endpoint::getResultKey)
- .containsExactly(tuple(EndpointType.MATCH, "LINE1", "result"));
+ .extracting(Endpoint::getType, Endpoint::getValue, Endpoint::getResultKey)
+ .containsExactly(tuple(EndpointType.MATCH, "LINE1", "result"));
// add key2 to the input map and there should be a match
input.put("key2", "7");
assertThat(DecisionEngine.matchTable(matchTable, input))
- .extracting(Endpoint::getType, Endpoint::getValue, Endpoint::getResultKey)
- .containsExactly(tuple(EndpointType.MATCH, "LINE2", "result"));
+ .extracting(Endpoint::getType, Endpoint::getValue, Endpoint::getResultKey)
+ .containsExactly(tuple(EndpointType.MATCH, "LINE2", "result"));
// if searching on key1 only, even though key2 was supplied should still match to first line
assertThat(DecisionEngine.matchTable(matchTable, input, new HashSet<>(Collections.singletonList("key1"))))
- .extracting(Endpoint::getType, Endpoint::getValue, Endpoint::getResultKey)
- .containsExactly(tuple(EndpointType.MATCH, "LINE1", "result"));
+ .extracting(Endpoint::getType, Endpoint::getValue, Endpoint::getResultKey)
+ .containsExactly(tuple(EndpointType.MATCH, "LINE1", "result"));
// supply an empty set of keys (the same meaning as not passing any keys
assertThat(DecisionEngine.matchTable(matchTable, input, new HashSet<>()))
- .extracting(Endpoint::getType, Endpoint::getValue, Endpoint::getResultKey)
- .containsExactly(tuple(EndpointType.MATCH, "LINE1", "result"));
+ .extracting(Endpoint::getType, Endpoint::getValue, Endpoint::getResultKey)
+ .containsExactly(tuple(EndpointType.MATCH, "LINE1", "result"));
// supply an invalid key. I think this should find nothing, but for the moment finds a match to the first row since none of the cells were
// compared to. It is the same as matching to a table with no INPUTS which would currently find a match to the first row.
assertThat(DecisionEngine.matchTable(matchTable, input, new HashSet<>(Collections.singletonList("bad_key"))))
- .extracting(Endpoint::getType, Endpoint::getValue, Endpoint::getResultKey)
- .containsExactly(tuple(EndpointType.MATCH, "LINE1", "result"));
+ .extracting(Endpoint::getType, Endpoint::getValue, Endpoint::getResultKey)
+ .containsExactly(tuple(EndpointType.MATCH, "LINE1", "result"));
}
@Test
@@ -537,6 +539,10 @@ void testMatchTableWithNoRows() {
input.put("a", "");
List extends Endpoint> endpoints = DecisionEngine.matchTable(tableMissing, input);
assertNull(endpoints);
+
+ // A malformed table with no parsed row collection also has no match.
+ ((StagingTable) tableMissing).setTableRows(null);
+ assertNull(DecisionEngine.matchTable(tableMissing, input));
}
@Test
@@ -584,7 +590,9 @@ void testValueKeyReferences() {
schema.setSchemaSelectionTable("table_selection");
schema.addInput("a");
schema.addInput("b");
- schema.addMapping(new StagingMapping("m1", Collections.singletonList(new StagingTablePath("table_key_references"))));
+ schema.addMapping(
+ new StagingMapping("m1", Collections.singletonList(new StagingTablePath("table_key_references")))
+ );
provider.addSchema(schema);
DecisionEngine engine = new DecisionEngine(provider);
@@ -809,7 +817,7 @@ void testMissingParameters() {
void testParameterLookupValidation() {
Map input = new HashMap<>();
input.put("a", "3");
- input.put("b", "31"); // value is not in lookup table
+ input.put("b", "31"); // value is not in lookup table
Result result = _ENGINE.process("starting_sample", input);
@@ -836,7 +844,7 @@ void testParameterLookupValidation() {
void testSingleTableProcess() {
Map input = new HashMap<>();
input.put("a", "7");
- input.put("b", "03"); // should map to "hemeretic" without using second table
+ input.put("b", "03"); // should map to "hemeretic" without using second table
input.put("e", "X");
Result result = _ENGINE.process("starting_sample", input);
@@ -917,7 +925,10 @@ void testProcessError() {
result = _ENGINE.process("starting_sample", input);
assertEquals(1, result.getErrors().size());
assertEquals(2, result.getPath().size());
- assertEquals("Matching resulted in an error in table 'table_sample_first' for column 'result' (8,99)", result.getErrors().getFirst().getMessage());
+ assertEquals(
+ "Matching resulted in an error in table 'table_sample_first' for column 'result' (8,99)",
+ result.getErrors().getFirst().getMessage()
+ );
assertNull(result.getErrors().getFirst().getKey());
assertEquals("table_sample_first", result.getErrors().getFirst().getTable());
assertEquals(Collections.singletonList("result"), result.getErrors().getFirst().getColumns());
@@ -933,6 +944,7 @@ void testProcessWithNullValues() {
table.addRawRow("1", "VALUE:FOUND1");
table.addRawRow("2", "VALUE");
table.addRawRow("3", "VALUE:");
+ table.addRawRow("4", "ERROR");
table.addRawRow("*", "MATCH");
provider.addTable(table);
@@ -942,7 +954,9 @@ void testProcessWithNullValues() {
inputKey.setDefault("0");
schema.addInput(inputKey);
schema.addInitialContext("result", "0");
- schema.addMapping(new StagingMapping("m1", Collections.singletonList(new StagingTablePath("table_null_values"))));
+ StagingTablePath nullValuePath = new StagingTablePath("table_null_values");
+ nullValuePath.addOutputMapping("other", "mapped");
+ schema.addMapping(new StagingMapping("m1", Collections.singletonList(nullValuePath)));
provider.addSchema(schema);
DecisionEngine engine = new DecisionEngine(provider);
@@ -952,6 +966,12 @@ void testProcessWithNullValues() {
assertFalse(result.hasErrors());
assertEquals("0", input.get("result"));
+ input.clear();
+ input.put("a", null);
+ result = engine.process("starting_null_values", input);
+ assertFalse(result.hasErrors());
+ assertEquals("0", input.get("result"));
+
input.clear();
input.put("a", "1");
result = engine.process("starting_null_values", input);
@@ -969,6 +989,20 @@ void testProcessWithNullValues() {
result = engine.process("starting_null_values", input);
assertFalse(result.hasErrors());
assertEquals("", input.get("result"));
+
+ input.clear();
+ input.put("a", "4");
+ result = engine.process("starting_null_values", input);
+ assertThat(result.getErrors())
+ .extracting(Error::getType, Error::getMessage, Error::getTable, Error::getColumns)
+ .containsExactly(
+ tuple(
+ Error.Type.STAGING_ERROR,
+ "Matching resulted in an error in table 'table_null_values' for column 'result' (4)",
+ "table_null_values",
+ Collections.singletonList("result")
+ )
+ );
}
@Test
@@ -1028,7 +1062,12 @@ void testStopIsNotOverwrittenByLaterJump() {
StagingSchema schema = new StagingSchema("stop_then_jump");
schema.setSchemaSelectionTable("table_stop_then_jump");
schema.addInput("input");
- schema.addMapping(new StagingMapping("m1", Arrays.asList(new StagingTablePath("table_stop_then_jump"), new StagingTablePath("table_after_stop"))));
+ schema.addMapping(
+ new StagingMapping(
+ "m1",
+ Arrays.asList(new StagingTablePath("table_stop_then_jump"), new StagingTablePath("table_after_stop"))
+ )
+ );
provider.addSchema(schema);
Map context = new HashMap<>();
@@ -1393,6 +1432,10 @@ void testMappedInclusionsAndExclusionsAndMissingTables() {
context.put("source", "B");
assertTrue(engine.isMappingInvolved(exclusion, context));
+ context.clear();
+ assertFalse(engine.isMappingInvolved(inclusion, context));
+ assertTrue(engine.isMappingInvolved(exclusion, context));
+
assertThrows(IllegalStateException.class, () -> engine.isMappingInvolved(inclusion, null));
StagingSchema schema = new StagingSchema("schema");
assertThrows(IllegalStateException.class, () -> engine.getInvolvedMappings(schema, null));
@@ -1406,12 +1449,20 @@ void testMappedInclusionsAndExclusionsAndMissingTables() {
@Test
void testMissingSchemaAndTableReferences() {
InMemoryDataProvider provider = new InMemoryDataProvider("Test", "1.0");
+ StagingTable table = new StagingTable("null_output_key");
+ table.addColumnDefinition(null, ColumnType.ENDPOINT);
+ provider.addTable(table);
DecisionEngine engine = new DecisionEngine(provider);
assertThrows(IllegalStateException.class, () -> engine.process("missing", new HashMap<>()));
assertThrows(IllegalStateException.class, () -> engine.getInvolvedTables("missing"));
assertTrue(engine.getInputs(new StagingTablePath("missing")).isEmpty());
assertTrue(engine.getOutputs(new StagingTablePath("missing")).isEmpty());
+ assertTrue(engine.getInputs((StagingTablePath) null).isEmpty());
+ assertTrue(engine.getOutputs((StagingTablePath) null).isEmpty());
+ assertTrue(engine.getInputs(new StagingMapping("empty"), new HashSet<>()).isEmpty());
+ assertTrue(engine.getOutputs(new StagingMapping("empty")).isEmpty());
+ assertTrue(engine.getOutputs(new StagingTablePath("null_output_key")).isEmpty());
StagingTablePath path = new StagingTablePath("starting_table");
Result result = new Result(new HashMap<>());
@@ -1445,7 +1496,10 @@ void testSchemaInputs() {
assertEquals(asSet("a", "b", "c"), _ENGINE.getInputs(provider.getSchema("starting_inclusions")));
assertEquals(asSet("a"), _ENGINE.getInputs(provider.getSchema("starting_recursion")));
assertEquals(asSet("a", "b", "c"), _ENGINE.getInputs(provider.getSchema("starting_multiple_endpoints")));
- assertEquals(asSet("b", "not_in_input_list"), _ENGINE.getInputs(provider.getSchema("starting_inclusions_extra_inputs")));
+ assertEquals(
+ asSet("b", "not_in_input_list"),
+ _ENGINE.getInputs(provider.getSchema("starting_inclusions_extra_inputs"))
+ );
assertEquals(asSet("main_input"), _ENGINE.getInputs(provider.getSchema("starting_intermediate_values")));
}
@@ -1457,9 +1511,18 @@ void testGetSchemaOutputs() {
assertEquals(asSet("result", "shared_result"), _ENGINE.getOutputs(provider.getSchema("starting_sample")));
assertEquals(asSet("result", "special"), _ENGINE.getOutputs(provider.getSchema("starting_inclusions")));
assertEquals(asSet("result"), _ENGINE.getOutputs(provider.getSchema("starting_recursion")));
- assertEquals(asSet("result", "r1", "r2", "r3"), _ENGINE.getOutputs(provider.getSchema("starting_multiple_endpoints")));
- assertEquals(asSet("mapped_result"), _ENGINE.getOutputs(provider.getSchema("starting_inclusions_extra_inputs")));
- assertEquals(asSet("intermediate_output", "final_output"), _ENGINE.getOutputs(provider.getSchema("starting_intermediate_values")));
+ assertEquals(
+ asSet("result", "r1", "r2", "r3"),
+ _ENGINE.getOutputs(provider.getSchema("starting_multiple_endpoints"))
+ );
+ assertEquals(
+ asSet("mapped_result"),
+ _ENGINE.getOutputs(provider.getSchema("starting_inclusions_extra_inputs"))
+ );
+ assertEquals(
+ asSet("intermediate_output", "final_output"),
+ _ENGINE.getOutputs(provider.getSchema("starting_intermediate_values"))
+ );
}
@Test
@@ -1617,6 +1680,15 @@ void testDefaultInputValidation() {
schema.addInput(input);
provider.addSchema(schema);
+ schema = new StagingSchema("invalid_required_continue");
+ schema.setSchemaSelectionTable("valid_inputs");
+ schema.setOnInvalidInput(Schema.StagingInputErrorHandler.CONTINUE);
+ input = new StagingSchemaInput("input", "input", "valid_inputs");
+ input.setDefault("X");
+ input.setUsedForStaging(true);
+ schema.addInput(input);
+ provider.addSchema(schema);
+
schema = new StagingSchema("invalid_non_required_default");
schema.setSchemaSelectionTable("valid_inputs");
schema.setOnInvalidInput(Schema.StagingInputErrorHandler.FAIL_WHEN_USED_FOR_STAGING);
@@ -1650,6 +1722,10 @@ void testDefaultInputValidation() {
assertEquals(Type.FAILED_INPUT, result.getType());
assertEquals(Error.Type.INVALID_REQUIRED_INPUT, result.getErrors().getFirst().getType());
+ result = engine.process("invalid_required_continue", new HashMap<>());
+ assertEquals(Type.STAGED, result.getType());
+ assertEquals(Error.Type.INVALID_REQUIRED_INPUT, result.getErrors().getFirst().getType());
+
result = engine.process("invalid_non_required_default", new HashMap<>());
assertEquals(Type.STAGED, result.getType());
assertEquals(Error.Type.INVALID_NON_REQUIRED_INPUT, result.getErrors().getFirst().getType());
@@ -1660,6 +1736,27 @@ void testDefaultInputValidation() {
assertEquals("X", result.getContext().get("input"));
}
+ @Test
+ void testDefaultTableMatchWithoutRequestedValue() {
+ InMemoryDataProvider provider = new InMemoryDataProvider("Test", "1.0");
+ StagingTable table = new StagingTable("malformed_default");
+ table.addColumnDefinition("selector", ColumnType.INPUT);
+ table.addColumnDefinition("other", ColumnType.ENDPOINT);
+ table.addRawRow("*", "VALUE:fallback");
+ provider.addTable(table);
+
+ StagingSchemaInput input = new StagingSchemaInput("requested");
+ input.setDefaultTable("malformed_default");
+ Result result = new Result(new HashMap<>());
+
+ assertEquals("", new DecisionEngine(provider).getDefault(input, result.getContext(), result));
+ assertThat(result.getErrors())
+ .extracting(Error::getType, Error::getKey, Error::getMessage)
+ .containsExactly(
+ tuple(Error.Type.MATCH_NOT_FOUND, "requested", "Default table malformed_default did not find a match")
+ );
+ }
+
@Test
void testProcessingReferenceErrorsAndMappingInitialContext() {
InMemoryDataProvider provider = new InMemoryDataProvider("Test", "1.0");
@@ -1817,6 +1914,7 @@ void testMappingInputsWithReferenceInTable() {
table.addRawRow("001", "VALUE:000");
table.addRawRow("002", "VALUE:{{input1}}");
provider.addTable(table);
+ table.setExtraInput(new HashSet<>(Arrays.asList("input1", "unmapped", "output1")));
StagingSchema schema = new StagingSchema("sample_outputs");
schema.setSchemaSelectionTable("table_selection");
@@ -1832,7 +1930,17 @@ void testMappingInputsWithReferenceInTable() {
DecisionEngine engine = new DecisionEngine(provider);
- assertEquals(new HashSet<>(Collections.singletonList("remapped1")), engine.getInputs(schema.getMappings().getFirst().getTablePaths().getFirst()));
+ assertEquals(
+ new HashSet<>(Arrays.asList("remapped1", "unmapped")),
+ engine.getInputs(schema.getMappings().getFirst().getTablePaths().getFirst())
+ );
+
+ StagingTable sharedKeyTable = new StagingTable("shared_input_output");
+ sharedKeyTable.addColumnDefinition("shared", ColumnType.INPUT);
+ sharedKeyTable.addColumnDefinition("shared", ColumnType.ENDPOINT);
+ provider.addTable(sharedKeyTable);
+
+ assertEquals(Collections.singleton("shared"), engine.getInputs(new StagingTablePath("shared_input_output")));
}
@Test
@@ -1891,7 +1999,12 @@ void testDefaultTable() {
// first, verify getInvolvedTables is working with default tables
Set tables = engine.getInvolvedTables("test_default_table");
- assertThat(tables).containsExactlyInAnyOrder("table_input1", "table_input2", "table_input2_default", "table_mapping");
+ assertThat(tables).containsExactlyInAnyOrder(
+ "table_input1",
+ "table_input2",
+ "table_input2_default",
+ "table_mapping"
+ );
// test a case where the default_table make a successful lookup
Map context = new HashMap<>();
@@ -1928,7 +2041,11 @@ void testDefaultTable() {
assertFalse(result1.hasErrors());
// test a case where the default_table did not exist
- schema.getInputs().stream().filter(i -> i.getDefaultTable() != null).forEach(i -> i.setDefaultTable("does_not_exist"));
+ schema
+ .getInputs()
+ .stream()
+ .filter(i -> i.getDefaultTable() != null)
+ .forEach(i -> i.setDefaultTable("does_not_exist"));
context = new HashMap<>();
context.put("input1", "000");
result = engine.process("test_default_table", context);
@@ -1947,7 +2064,11 @@ void testDefaultTable() {
assertEquals("Default table does not exist: does_not_exist", result1.getErrors().getFirst().getMessage());
// test a case where the default table did not find a match
- schema.getInputs().stream().filter(i -> i.getDefaultTable() != null).forEach(i -> i.setDefaultTable("table_input2_default"));
+ schema
+ .getInputs()
+ .stream()
+ .filter(i -> i.getDefaultTable() != null)
+ .forEach(i -> i.setDefaultTable("table_input2_default"));
provider.getTable("table_input2_default").setRawRows(new ArrayList<>());
provider.initTable(provider.getTable("table_input2_default"));
context = new HashMap<>();
@@ -1956,7 +2077,10 @@ void testDefaultTable() {
assertEquals(Type.STAGED, result.getType());
assertEquals(1, result.getErrors().size());
assertEquals("input2", result.getErrors().getFirst().getKey());
- assertEquals("Default table table_input2_default did not find a match", result.getErrors().getFirst().getMessage());
+ assertEquals(
+ "Default table table_input2_default did not find a match",
+ result.getErrors().getFirst().getMessage()
+ );
// check same case with getDefault method
context = new HashMap<>();
@@ -1965,7 +2089,9 @@ void testDefaultTable() {
assertEquals("", engine.getDefault(input2, context, result1));
assertEquals(1, result1.getErrors().size());
assertEquals("input2", result.getErrors().getFirst().getKey());
- assertEquals("Default table table_input2_default did not find a match", result.getErrors().getFirst().getMessage());
+ assertEquals(
+ "Default table table_input2_default did not find a match",
+ result.getErrors().getFirst().getMessage()
+ );
}
-
}
diff --git a/src/test/java/com/imsweb/staging/entities/SchemaLookupTest.java b/src/test/java/com/imsweb/staging/entities/SchemaLookupTest.java
index eaba0a5b2..b35032588 100644
--- a/src/test/java/com/imsweb/staging/entities/SchemaLookupTest.java
+++ b/src/test/java/com/imsweb/staging/entities/SchemaLookupTest.java
@@ -1,13 +1,17 @@
package com.imsweb.staging.entities;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
import java.util.HashSet;
+import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
-
import org.junit.jupiter.api.Test;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
class SchemaLookupTest {
@Test
@@ -25,4 +29,96 @@ void testConstructorMissingValues() {
assertEquals(new SchemaLookup("C629", "9100").getKeys(), Stream.of("site", "hist").collect(Collectors.toSet()));
}
-}
\ No newline at end of file
+ @Test
+ void testSiteAndHistologyMutation() {
+ SchemaLookup lookup = new SchemaLookup("C629", "9100");
+
+ lookup.setSite("C509");
+ lookup.setHistology("8000");
+
+ assertEquals("C509", lookup.getSite());
+ assertEquals("C509", lookup.getInput(StagingData.PRIMARY_SITE_KEY));
+ assertEquals("8000", lookup.getHistology());
+ assertEquals("8000", lookup.getInput(StagingData.HISTOLOGY_KEY));
+ }
+
+ @Test
+ void testEqualsAndHashCode() {
+ SchemaLookup lookup1 = new SchemaLookup("C629", "9100");
+ SchemaLookup lookup2 = new SchemaLookup("C629", "9100");
+ SchemaLookup lookup3 = new SchemaLookup("C629", "9100");
+
+ assertEquals(lookup1, lookup1);
+ assertEquals(lookup1, lookup2);
+ assertEquals(lookup2, lookup1);
+ assertEquals(lookup2, lookup3);
+ assertEquals(lookup1, lookup3);
+ assertEquals(lookup1.hashCode(), lookup2.hashCode());
+ }
+
+ @Test
+ void testNotEquals() {
+ SchemaLookup lookup = new SchemaLookup("C629", "9100");
+
+ assertNotEquals(null, lookup);
+ assertNotEquals("C629", lookup);
+ assertNotEquals(lookup, new TestSchemaLookup("C629", "9100"));
+ assertNotEquals(new SchemaLookup("C509", "9100"), lookup);
+ assertNotEquals(new SchemaLookup("C629", "8000"), lookup);
+ }
+
+ @Test
+ void testClearInputs() {
+ TestSchemaLookup lookup = new TestSchemaLookup("C629", "9100");
+ lookup.setInput("allowed", "value");
+
+ lookup.clear();
+
+ assertTrue(lookup.getInputs().isEmpty());
+ }
+
+ @Test
+ void testAllowedKeys() {
+ SchemaLookup unrestrictedLookup = new SchemaLookup();
+ unrestrictedLookup.setInput("anything", "value");
+ assertEquals("value", unrestrictedLookup.getInput("anything"));
+
+ TestSchemaLookup restrictedLookup = new TestSchemaLookup();
+ restrictedLookup.setInput("allowed", "value");
+ assertEquals("value", restrictedLookup.getInput("allowed"));
+ assertThrows(IllegalStateException.class, () -> restrictedLookup.setInput("disallowed", "value"));
+ }
+
+ @Test
+ void testDiscriminator() {
+ SchemaLookup lookup = new SchemaLookup("C629", "9100");
+ assertFalse(lookup.hasDiscriminator());
+
+ lookup.setInput("discriminator", null);
+ assertFalse(lookup.hasDiscriminator());
+
+ lookup.setInput("discriminator", "");
+ assertFalse(lookup.hasDiscriminator());
+
+ lookup.setInput("discriminator", "001");
+ assertTrue(lookup.hasDiscriminator());
+ }
+
+ private static class TestSchemaLookup extends SchemaLookup {
+
+ TestSchemaLookup() {}
+
+ TestSchemaLookup(String site, String histology) {
+ super(site, histology);
+ }
+
+ @Override
+ public Set getAllowedKeys() {
+ return Set.of(StagingData.PRIMARY_SITE_KEY, StagingData.HISTOLOGY_KEY, "allowed");
+ }
+
+ void clear() {
+ clearInputs();
+ }
+ }
+}