diff --git a/src/org/labkey/test/tests/TextChoiceSampleTypeTest.java b/src/org/labkey/test/tests/TextChoiceSampleTypeTest.java index fb24f349be..658fbe47be 100644 --- a/src/org/labkey/test/tests/TextChoiceSampleTypeTest.java +++ b/src/org/labkey/test/tests/TextChoiceSampleTypeTest.java @@ -15,12 +15,20 @@ */ package org.labkey.test.tests; +import org.json.JSONArray; +import org.json.JSONObject; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.junit.experimental.categories.Category; import org.labkey.junit.LabKeyAssert; import org.labkey.remoteapi.CommandException; +import org.labkey.remoteapi.Connection; +import org.labkey.remoteapi.domain.Domain; +import org.labkey.remoteapi.domain.PropertyDescriptor; +import org.labkey.remoteapi.domain.SaveDomainCommand; +import org.labkey.remoteapi.query.ContainerFilter; +import org.labkey.remoteapi.query.SelectRowsCommand; import org.labkey.test.BaseWebDriverTest; import org.labkey.test.Locator; import org.labkey.test.categories.Daily; @@ -33,6 +41,7 @@ import org.labkey.test.params.experiment.SampleTypeDefinition; import org.labkey.test.util.AuditLogHelper; import org.labkey.test.util.DataRegionTable; +import org.labkey.test.util.EscapeUtil; import org.labkey.test.util.PortalHelper; import org.labkey.test.util.SampleTypeHelper; import org.labkey.test.util.TestDataGenerator; @@ -728,4 +737,94 @@ public void testSetTextChoiceValueForSample() throws IOException, CommandExcepti String.format("Value '%s' for field '%s' is invalid.", invalidValue, textChoiceFieldCaption), errorMsg.getText()); } + /** + *

+ * Rename TextChoice values in use by more rows than the server updates in one batch. + *

+ *

+ * This test will: + *

+ *

+ */ + @Test + public void testChainedValueUpdatesAcrossBatches() throws IOException, CommandException + { + String sampleTypeName = "Test_TC_Chained_Updates"; + String textChoiceFieldName = "StatusChoice"; + String subfolder = "ChainedUpdatesSubfolder"; + String subfolderPath = getProjectName() + "/" + subfolder; + Map valueUpdates = Map.of("A", "B", "B", "C", "C", "D"); + // More than one server-side batch (1000 rows) for A and B + Map rowCounts = Map.of("A", 1100, "B", 1100, "C", 300); + + _containerHelper.createSubfolder(getProjectName(), subfolder); + + FieldDefinition textChoiceField = new FieldDefinition(textChoiceFieldName, ColumnType.TextChoice) + .setTextChoiceValues(List.of("A", "B", "C")); + TestDataGenerator dataGenerator = SampleTypeAPIHelper.createEmptySampleType(getProjectName(), + new SampleTypeDefinition(sampleTypeName).setFields(List.of(textChoiceField))); + + log("Insert samples, alternating between the project and the subfolder so each batch spans both containers."); + List> projectRows = new ArrayList<>(); + List> subfolderRows = new ArrayList<>(); + for (Map.Entry entry : rowCounts.entrySet()) + { + for (int i = 0; i < entry.getValue(); i++) + { + Map row = Map.of("Name", entry.getKey() + "-" + i, textChoiceFieldName, entry.getKey()); + (i % 2 == 0 ? projectRows : subfolderRows).add(row); + } + } + Connection cn = createDefaultConnection(); + dataGenerator.getQueryHelper(cn, getProjectName()).insertRows(projectRows); + dataGenerator.getQueryHelper(cn, subfolderPath).insertRows(subfolderRows); + + log("Rename A -> B, B -> C and C -> D in one domain save."); + Domain domain = dataGenerator.getQueryHelper(cn).getDomainDetails().getDomain(); + List fields = new ArrayList<>(); + for (PropertyDescriptor field : domain.getFields()) + { + if (!textChoiceFieldName.equals(field.getName())) + { + fields.add(field); + continue; + } + + JSONObject fieldJson = field.toJSONObject(); + // toJSONObject() passes through the unmodifiable validator list, so copy it to editable JSON + JSONArray validators = new JSONArray((List) field.getAllProperties().get("propertyValidators")); + fieldJson.put("propertyValidators", validators); + JSONObject validator = validators.getJSONObject(0); + validator.put("expression", EscapeUtil.getTextChoiceValidatorExpression(List.of("B", "C", "D"))); + validator.put("extraProperties", Map.of("valueUpdates", valueUpdates)); + fields.add(new PropertyDescriptor(fieldJson)); + } + domain.setFields(fields); + + SaveDomainCommand saveCmd = new SaveDomainCommand("samples", sampleTypeName); + saveCmd.setDomainDesign(domain); + saveCmd.execute(cn, getProjectName()); + + log("Verify each sample was mapped from its original value."); + SelectRowsCommand selectCmd = new SelectRowsCommand("samples", sampleTypeName); + selectCmd.setColumns(List.of("Name", textChoiceFieldName)); + selectCmd.setContainerFilter(ContainerFilter.CurrentAndSubfolders); + List> rows = selectCmd.execute(cn, getProjectName()).getRows(); + + checker().verifyEquals("Unexpected number of samples.", rowCounts.values().stream().mapToInt(Integer::intValue).sum(), rows.size()); + Map mismatches = new HashMap<>(); + for (Map row : rows) + { + String originalValue = ((String) row.get("Name")).split("-")[0]; + String expectedValue = valueUpdates.get(originalValue); + if (!expectedValue.equals(row.get(textChoiceFieldName))) + mismatches.merge(originalValue + " -> " + row.get(textChoiceFieldName), 1, Integer::sum); + } + checker().verifyTrue("Samples not mapped exactly once from their original value: " + mismatches, mismatches.isEmpty()); + } + }