From da389b0553223a98ff1e2116e4764dcc224710ea Mon Sep 17 00:00:00 2001 From: labkey-danield Date: Thu, 21 Aug 2025 17:08:12 -0700 Subject: [PATCH 1/6] Add test coverage for issue 53421 --- .../labkey/test/tests/LinkedSchemaTest.java | 102 ++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/src/org/labkey/test/tests/LinkedSchemaTest.java b/src/org/labkey/test/tests/LinkedSchemaTest.java index 88b4b2e43b..d596f48956 100644 --- a/src/org/labkey/test/tests/LinkedSchemaTest.java +++ b/src/org/labkey/test/tests/LinkedSchemaTest.java @@ -25,6 +25,7 @@ import org.labkey.test.SortDirection; import org.labkey.test.TestFileUtils; import org.labkey.test.TestTimeoutException; +import org.labkey.test.WebTestHelper; import org.labkey.test.categories.Daily; import org.labkey.test.categories.Data; import org.labkey.test.components.CustomizeView; @@ -32,6 +33,8 @@ import org.labkey.test.pages.list.GridPage; import org.labkey.test.pages.query.QueryMetadataEditorPage; import org.labkey.test.params.FieldDefinition; +import org.labkey.test.params.FieldInfo; +import org.labkey.test.params.experiment.SampleTypeDefinition; import org.labkey.test.params.list.IntListDefinition; import org.labkey.test.params.list.ListDefinition; import org.labkey.test.params.list.VarListDefinition; @@ -41,6 +44,8 @@ import org.labkey.test.util.DataRegionTable; import org.labkey.test.util.LogMethod; import org.labkey.test.util.SchemaHelper; +import org.labkey.test.util.TestDataGenerator; +import org.labkey.test.util.exp.SampleTypeAPIHelper; import java.io.File; import java.io.IOException; @@ -112,6 +117,7 @@ public class LinkedSchemaTest extends BaseWebDriverTest private static final String STUDY_FOLDER = "StudyFolder"; // Folder used to validate fix for issues 32454 & 32456 private static final String MOTHER = "Mother"; private static final String READER_USER = "reader@linkedschema.test"; + private static final String EXTERNAL_SCHEMA_USER = "external@linkedschema.test"; private final AuditLogHelper _auditLogHelper = new AuditLogHelper(this); public static final String LIST_NAME = "LinkedSchemaTestPeople"; @@ -401,6 +407,7 @@ protected void doCleanup(boolean afterTest) throws TestTimeoutException { super.doCleanup(afterTest); _userHelper.deleteUsers(false, READER_USER); + _userHelper.deleteUsers(false, EXTERNAL_SCHEMA_USER); } @BeforeClass @@ -632,8 +639,13 @@ void setupProject() { ApiPermissionsHelper apiPermissionsHelper = new ApiPermissionsHelper(this); _userHelper.createUser(READER_USER); + _userHelper.createUser(EXTERNAL_SCHEMA_USER); _containerHelper.createProject(getProjectName(), null); + + // Giving this user more permissions in the test project so it can access the schema browser. + apiPermissionsHelper.setUserPermissions(EXTERNAL_SCHEMA_USER, "Folder Administrator"); + _containerHelper.createSubfolder(getProjectName(), SOURCE_FOLDER); // Enable linkedschematest in source folder so the "BPeopleTemplate" is visible. _containerHelper.enableModule("linkedschematest"); @@ -918,6 +930,96 @@ public void testCoreLinkedSchemaFilters() _schemaHelper.deleteSchema(sourceContainerPath, linkedSchemaName); } + // Issue 53421: linked schemas can't use sample type as a source in a container different from where the type is defined + @Test + public void testLinkedSchemaToExternalSubfolder() throws IOException, CommandException + { + + String externalProject = "Linked Schema Other Project"; + String subFolder = "Sub Folder 01"; + String externalPath = externalProject + "/" + subFolder; + + log(String.format("Create a separate project %s with sub-folder %s.", + externalProject, subFolder)); + + // Delete the external project if it already exists. + _containerHelper.deleteProject(externalProject, false); + _containerHelper.createProject(externalProject, null); + _containerHelper.createSubfolder(externalProject, subFolder); + + String sampleType = "Linked Schema Test"; + FieldInfo strField = new FieldInfo(TestDataGenerator.randomFieldName("Str"), FieldDefinition.ColumnType.String); + SampleTypeDefinition sampleTypeDefinition = new SampleTypeDefinition(sampleType); + sampleTypeDefinition.setNameExpression("External ${genId}"); + sampleTypeDefinition.addField(new FieldDefinition(strField.getName())); + + log(String.format("In project %s create a sample type %s.", + externalProject, sampleType)); + + TestDataGenerator testDataGenerator = SampleTypeAPIHelper.createEmptySampleType(externalProject, sampleTypeDefinition); + + log(String.format("In project %s add samples to the sample type.", + externalProject)); + + List parentFolderValues = List.of("A", "B", "C", "D"); + for (String value : parentFolderValues) + { + testDataGenerator.addCustomRow(Map.of(strField.getName(), value)); + } + testDataGenerator.insertRows(WebTestHelper.getRemoteApiConnection(), testDataGenerator.getRows()); + + log(String.format("From the sub-folder %s add samples to the sample type.", + externalPath)); + + List subFolderValues = List.of("W", "X", "Y", "Z"); + testDataGenerator = new TestDataGenerator("samples", sampleType, externalProject + "/" + subFolder); + for (String value : subFolderValues) + { + testDataGenerator.addCustomRow(Map.of(strField.getName(), value)); + } + testDataGenerator.insertRows(WebTestHelper.getRemoteApiConnection(), testDataGenerator.getRows()); + + // It looks like the createProject call puts you on the new project. + log("Go back to the test project."); + goToProjectHome(); + + String linkedSchemaName = "External_Project_Schema"; + log(String.format("Create a linked schema named %s that looks at %s.", + linkedSchemaName, externalPath)); + + _schemaHelper.createLinkedSchema(getProjectName(), linkedSchemaName, externalPath, null, "samples", null, null); + + goToProjectHome(); + + log(String.format("Use the schema browser to validate only the samples in the sub-folder %s are visible.", + externalPath)); + + validateDataFromExternalProject(linkedSchemaName, sampleType, strField, + subFolderValues, String.format("Data displayed for linked schema '%s' not as expected.", linkedSchemaName)); + + log("Impersonate a user, reader in the current project, with no permissions in the external project."); + + goToProjectHome(); + impersonate(EXTERNAL_SCHEMA_USER); + validateDataFromExternalProject(linkedSchemaName, sampleType, strField, + subFolderValues, String.format("User with no permissions did not see the expected data for the linked schema '%s'.", linkedSchemaName)); + + stopImpersonating(); + } + + private void validateDataFromExternalProject(String linkedSchemaName, String sampleType, FieldInfo strField, + List expectedValues, String errorMsg) + { + goToSchemaBrowser(); + viewQueryData(linkedSchemaName, sampleType); + DataRegionTable table = new DataRegionTable("query", getDriver()); + List actualValues = table.getColumnDataAsText(strField); + + checker().withScreenshot().verifyEqualsSorted(errorMsg, + expectedValues, actualValues); + + } + /* Test coverage : Issue 45347: Audit table data not available in linked schema */ From acb046f8adc13c92a08b6626962a97968e86a53f Mon Sep 17 00:00:00 2001 From: labkey-danield Date: Thu, 21 Aug 2025 17:17:32 -0700 Subject: [PATCH 2/6] Corrected a type and some comments. --- src/org/labkey/test/tests/LinkedSchemaTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/org/labkey/test/tests/LinkedSchemaTest.java b/src/org/labkey/test/tests/LinkedSchemaTest.java index d596f48956..fb0e0d0475 100644 --- a/src/org/labkey/test/tests/LinkedSchemaTest.java +++ b/src/org/labkey/test/tests/LinkedSchemaTest.java @@ -997,7 +997,7 @@ public void testLinkedSchemaToExternalSubfolder() throws IOException, CommandExc validateDataFromExternalProject(linkedSchemaName, sampleType, strField, subFolderValues, String.format("Data displayed for linked schema '%s' not as expected.", linkedSchemaName)); - log("Impersonate a user, reader in the current project, with no permissions in the external project."); + log("Impersonate a user, with no permissions in the external project, and validate the linked schema and data is as expected."); goToProjectHome(); impersonate(EXTERNAL_SCHEMA_USER); @@ -1015,6 +1015,7 @@ private void validateDataFromExternalProject(String linkedSchemaName, String sam DataRegionTable table = new DataRegionTable("query", getDriver()); List actualValues = table.getColumnDataAsText(strField); + // Using a sort list to validate because the list should be equal and I don't want to worry about the order. checker().withScreenshot().verifyEqualsSorted(errorMsg, expectedValues, actualValues); From 7d3d0b7dde02c38f24da12a6d9ecc7acda82c09e Mon Sep 17 00:00:00 2001 From: labkey-danield Date: Fri, 22 Aug 2025 16:59:47 -0700 Subject: [PATCH 3/6] Added checks to a linked data class and experiment (flow). Consolidated code. --- .../labkey/test/tests/LinkedSchemaTest.java | 172 +++++++++++++----- 1 file changed, 130 insertions(+), 42 deletions(-) diff --git a/src/org/labkey/test/tests/LinkedSchemaTest.java b/src/org/labkey/test/tests/LinkedSchemaTest.java index fb0e0d0475..d08fce3030 100644 --- a/src/org/labkey/test/tests/LinkedSchemaTest.java +++ b/src/org/labkey/test/tests/LinkedSchemaTest.java @@ -34,6 +34,7 @@ import org.labkey.test.pages.query.QueryMetadataEditorPage; import org.labkey.test.params.FieldDefinition; import org.labkey.test.params.FieldInfo; +import org.labkey.test.params.experiment.DataClassDefinition; import org.labkey.test.params.experiment.SampleTypeDefinition; import org.labkey.test.params.list.IntListDefinition; import org.labkey.test.params.list.ListDefinition; @@ -43,13 +44,16 @@ import org.labkey.test.util.AuditLogHelper; import org.labkey.test.util.DataRegionTable; import org.labkey.test.util.LogMethod; +import org.labkey.test.util.PortalHelper; import org.labkey.test.util.SchemaHelper; import org.labkey.test.util.TestDataGenerator; +import org.labkey.test.util.exp.DataClassAPIHelper; import org.labkey.test.util.exp.SampleTypeAPIHelper; import java.io.File; import java.io.IOException; import java.util.Arrays; +import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -936,84 +940,168 @@ public void testLinkedSchemaToExternalSubfolder() throws IOException, CommandExc { String externalProject = "Linked Schema Other Project"; - String subFolder = "Sub Folder 01"; - String externalPath = externalProject + "/" + subFolder; - - log(String.format("Create a separate project %s with sub-folder %s.", - externalProject, subFolder)); + log(String.format("Create a separate project %s.", + externalProject)); // Delete the external project if it already exists. _containerHelper.deleteProject(externalProject, false); _containerHelper.createProject(externalProject, null); - _containerHelper.createSubfolder(externalProject, subFolder); + + validateExternalLinkedSampleType(externalProject); + validateExternalLinkedExperiment(externalProject); // Flow. Is this right? + validateExternalLinkedDataclass(externalProject); + } + + private void validateExternalLinkedSampleType(String externalProject) throws IOException, CommandException + { + String subFolder = "Sub Folder Sample Type"; + String subFolderPath = externalProject + "/" + subFolder; String sampleType = "Linked Schema Test"; - FieldInfo strField = new FieldInfo(TestDataGenerator.randomFieldName("Str"), FieldDefinition.ColumnType.String); - SampleTypeDefinition sampleTypeDefinition = new SampleTypeDefinition(sampleType); - sampleTypeDefinition.setNameExpression("External ${genId}"); - sampleTypeDefinition.addField(new FieldDefinition(strField.getName())); + String strField = TestDataGenerator.randomFieldName("Str"); - log(String.format("In project %s create a sample type %s.", - externalProject, sampleType)); + log(String.format("Create sub-folder %s for the sample type test.", subFolderPath)); + _containerHelper.createSubfolder(externalProject, subFolder); - TestDataGenerator testDataGenerator = SampleTypeAPIHelper.createEmptySampleType(externalProject, sampleTypeDefinition); + List expectedValues = populateDomain(externalProject, subFolder, true, sampleType, strField); - log(String.format("In project %s add samples to the sample type.", - externalProject)); + String linkedSampleTypeSchema = "External_Sample_Type_Schema"; + validateExternalLinkedSchema(linkedSampleTypeSchema, subFolderPath, "samples", sampleType, strField, expectedValues); + + } + + private void validateExternalLinkedExperiment(String externalProject) + { + String subFolder = "Sub Folder Experiment"; + String subfolderPath = externalProject + "/" + subFolder; + + createExperiment(externalProject, subFolder); + + List expectedValues = List.of("Example 5 Run (XTandem peptide search)"); + String linkedExpRunSchema = "External_Exp_Run_Schema"; + + validateExternalLinkedSchema(linkedExpRunSchema, subfolderPath, "flow", "Runs", "Name", expectedValues); + + } + + private void validateExternalLinkedDataclass(String externalProject) throws IOException, CommandException + { + + String subFolder = "DataClass Sub Folder"; + String subfolderPath = externalProject + "/" + subFolder; + + String dataClassName = "Export data class"; + String strField = TestDataGenerator.randomFieldName("Str"); + + log(String.format("Create sub-folder %s for the data classs test.", subfolderPath)); + _containerHelper.createSubfolder(externalProject, subFolder); + + List expectedValues = populateDomain(externalProject, subFolder, false, dataClassName, strField); + + String linkedDataclassSchema = "External_Dataclass_Schema"; + validateExternalLinkedSchema(linkedDataclassSchema, subfolderPath, "exp.data", dataClassName, strField, expectedValues); - List parentFolderValues = List.of("A", "B", "C", "D"); - for (String value : parentFolderValues) + } + + // Populating a sample type and a data class is very similar. + private List populateDomain(String parentProject, String subFolder, boolean isSampleType, String query, String fieldName) throws IOException, CommandException + { + + TestDataGenerator testDataGenerator; + FieldDefinition field = new FieldDefinition(fieldName); + String subFolderPath = parentProject + "/" + subFolder; + String schema; + + if (isSampleType) { - testDataGenerator.addCustomRow(Map.of(strField.getName(), value)); + schema = "samples"; + SampleTypeDefinition sampleTypeDefinition = new SampleTypeDefinition(query); + sampleTypeDefinition.setNameExpression("External ${genId}"); + sampleTypeDefinition.addField(field); + testDataGenerator = SampleTypeAPIHelper.createEmptySampleType(parentProject, sampleTypeDefinition); + } + else + { + schema = "exp.data"; + DataClassDefinition dataClassDefinition = new DataClassDefinition(query); + dataClassDefinition.setNameExpression("DC - ${genId}"); + dataClassDefinition.addField(field); + testDataGenerator = DataClassAPIHelper.createEmptyDataClass(parentProject, dataClassDefinition); } - testDataGenerator.insertRows(WebTestHelper.getRemoteApiConnection(), testDataGenerator.getRows()); - log(String.format("From the sub-folder %s add samples to the sample type.", - externalPath)); + for (String value : List.of("A", "B", "C", "D")) + { + testDataGenerator.addCustomRow(Map.of(fieldName, value)); + } + testDataGenerator.insertRows(WebTestHelper.getRemoteApiConnection(), testDataGenerator.getRows()); + // Add entries in the subfolder. Send these back as values to check. List subFolderValues = List.of("W", "X", "Y", "Z"); - testDataGenerator = new TestDataGenerator("samples", sampleType, externalProject + "/" + subFolder); + testDataGenerator = new TestDataGenerator(schema, query, subFolderPath); for (String value : subFolderValues) { - testDataGenerator.addCustomRow(Map.of(strField.getName(), value)); + testDataGenerator.addCustomRow(Map.of(fieldName, value)); } testDataGenerator.insertRows(WebTestHelper.getRemoteApiConnection(), testDataGenerator.getRows()); - // It looks like the createProject call puts you on the new project. - log("Go back to the test project."); - goToProjectHome(); + return subFolderValues; + } - String linkedSchemaName = "External_Project_Schema"; - log(String.format("Create a linked schema named %s that looks at %s.", - linkedSchemaName, externalPath)); + private void createExperiment(String projectName, String subFolder) + { + // This is cut-and-paste code from ExpTest setup. + + _containerHelper.createSubfolder(projectName, subFolder, new String[]{"Experiment", "Query"}); + + new PortalHelper(this) + .doInAdminMode(portalHelper -> { + portalHelper.addWebPart("Data Pipeline"); + portalHelper.addWebPart("Run Groups"); + }); + clickButton("Setup"); + setPipelineRoot(TestFileUtils.getSampleData("xarfiles/expVerify").getAbsolutePath()); + clickFolder(subFolder); - _schemaHelper.createLinkedSchema(getProjectName(), linkedSchemaName, externalPath, null, "samples", null, null); + clickButton("Process and Import Data"); + + _fileBrowserHelper.importFile("experiment.xar.xml", "Import Experiment"); + Date importDate = new Date(); // Import timestamp will have various formats applied to it + clickAndWait(Locator.linkWithText("Data Pipeline")); + waitForPipelineJobsToComplete(1, false); + + } + private void validateExternalLinkedSchema(String linkedSchemaName, String subfolderPath, String sourceSchemaName, String sourceQueryName, String fieldName, List expectedValues) + { goToProjectHome(); - log(String.format("Use the schema browser to validate only the samples in the sub-folder %s are visible.", - externalPath)); + log(String.format("Create a linked schema named %s that looks at %s.", + linkedSchemaName, subfolderPath)); + + _schemaHelper.createLinkedSchema(getProjectName(), linkedSchemaName, subfolderPath, null, sourceSchemaName, null, null); - validateDataFromExternalProject(linkedSchemaName, sampleType, strField, - subFolderValues, String.format("Data displayed for linked schema '%s' not as expected.", linkedSchemaName)); + goToProjectHome(); - log("Impersonate a user, with no permissions in the external project, and validate the linked schema and data is as expected."); + validateExternalLinkedData(linkedSchemaName, sourceQueryName, fieldName, + expectedValues, String.format("Data displayed for linked schema '%s' not as expected.", linkedSchemaName)); + log("Impersonate a user, with no permissions in the external source project, and validate."); goToProjectHome(); impersonate(EXTERNAL_SCHEMA_USER); - validateDataFromExternalProject(linkedSchemaName, sampleType, strField, - subFolderValues, String.format("User with no permissions did not see the expected data for the linked schema '%s'.", linkedSchemaName)); - + validateExternalLinkedData(linkedSchemaName, sourceQueryName, fieldName, + expectedValues, String.format("User with no permissions did not see the expected data for the linked schema '%s'.", linkedSchemaName)); stopImpersonating(); } - private void validateDataFromExternalProject(String linkedSchemaName, String sampleType, FieldInfo strField, - List expectedValues, String errorMsg) + private void validateExternalLinkedData(String linkedSchemaName, String query, String fieldName, List expectedValues, String errorMsg) { + + // Use a FieldInfo object to deal with any tricky characters. + FieldInfo fieldInfo = new FieldInfo(fieldName, FieldDefinition.ColumnType.String); goToSchemaBrowser(); - viewQueryData(linkedSchemaName, sampleType); + viewQueryData(linkedSchemaName, query); DataRegionTable table = new DataRegionTable("query", getDriver()); - List actualValues = table.getColumnDataAsText(strField); + List actualValues = table.getColumnDataAsText(fieldInfo); // Using a sort list to validate because the list should be equal and I don't want to worry about the order. checker().withScreenshot().verifyEqualsSorted(errorMsg, From 259feb176a391ecfe9faf2bbba81f2330459c07c Mon Sep 17 00:00:00 2001 From: labkey-danield Date: Fri, 22 Aug 2025 17:07:44 -0700 Subject: [PATCH 4/6] Changing some variable names to add clarity. --- .../labkey/test/tests/LinkedSchemaTest.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/org/labkey/test/tests/LinkedSchemaTest.java b/src/org/labkey/test/tests/LinkedSchemaTest.java index d08fce3030..2fb791f6a8 100644 --- a/src/org/labkey/test/tests/LinkedSchemaTest.java +++ b/src/org/labkey/test/tests/LinkedSchemaTest.java @@ -1004,12 +1004,12 @@ private void validateExternalLinkedDataclass(String externalProject) throws IOEx } // Populating a sample type and a data class is very similar. - private List populateDomain(String parentProject, String subFolder, boolean isSampleType, String query, String fieldName) throws IOException, CommandException + private List populateDomain(String externalProject, String subFolder, boolean isSampleType, String query, String fieldName) throws IOException, CommandException { TestDataGenerator testDataGenerator; FieldDefinition field = new FieldDefinition(fieldName); - String subFolderPath = parentProject + "/" + subFolder; + String subFolderPath = externalProject + "/" + subFolder; String schema; if (isSampleType) @@ -1018,7 +1018,7 @@ private List populateDomain(String parentProject, String subFolder, bool SampleTypeDefinition sampleTypeDefinition = new SampleTypeDefinition(query); sampleTypeDefinition.setNameExpression("External ${genId}"); sampleTypeDefinition.addField(field); - testDataGenerator = SampleTypeAPIHelper.createEmptySampleType(parentProject, sampleTypeDefinition); + testDataGenerator = SampleTypeAPIHelper.createEmptySampleType(externalProject, sampleTypeDefinition); } else { @@ -1026,7 +1026,7 @@ private List populateDomain(String parentProject, String subFolder, bool DataClassDefinition dataClassDefinition = new DataClassDefinition(query); dataClassDefinition.setNameExpression("DC - ${genId}"); dataClassDefinition.addField(field); - testDataGenerator = DataClassAPIHelper.createEmptyDataClass(parentProject, dataClassDefinition); + testDataGenerator = DataClassAPIHelper.createEmptyDataClass(externalProject, dataClassDefinition); } for (String value : List.of("A", "B", "C", "D")) @@ -1047,11 +1047,11 @@ private List populateDomain(String parentProject, String subFolder, bool return subFolderValues; } - private void createExperiment(String projectName, String subFolder) + private void createExperiment(String externalProject, String subFolder) { // This is cut-and-paste code from ExpTest setup. - _containerHelper.createSubfolder(projectName, subFolder, new String[]{"Experiment", "Query"}); + _containerHelper.createSubfolder(externalProject, subFolder, new String[]{"Experiment", "Query"}); new PortalHelper(this) .doInAdminMode(portalHelper -> { @@ -1071,14 +1071,14 @@ private void createExperiment(String projectName, String subFolder) } - private void validateExternalLinkedSchema(String linkedSchemaName, String subfolderPath, String sourceSchemaName, String sourceQueryName, String fieldName, List expectedValues) + private void validateExternalLinkedSchema(String linkedSchemaName, String sourceSubFolderPath, String sourceSchemaName, String sourceQueryName, String fieldName, List expectedValues) { goToProjectHome(); log(String.format("Create a linked schema named %s that looks at %s.", - linkedSchemaName, subfolderPath)); + linkedSchemaName, sourceSubFolderPath)); - _schemaHelper.createLinkedSchema(getProjectName(), linkedSchemaName, subfolderPath, null, sourceSchemaName, null, null); + _schemaHelper.createLinkedSchema(getProjectName(), linkedSchemaName, sourceSubFolderPath, null, sourceSchemaName, null, null); goToProjectHome(); From 0f59fc3ad4206e3ebac5cd4d69422f1bd5e073b7 Mon Sep 17 00:00:00 2001 From: labkey-danield Date: Mon, 25 Aug 2025 14:51:26 -0700 Subject: [PATCH 5/6] Change permissions for EXTERNAL_SCHEMA_USER to be Reader. Change test for exp to be cleaner. Use more fuzz testing. --- .../labkey/test/tests/LinkedSchemaTest.java | 65 +++++++++---------- 1 file changed, 31 insertions(+), 34 deletions(-) diff --git a/src/org/labkey/test/tests/LinkedSchemaTest.java b/src/org/labkey/test/tests/LinkedSchemaTest.java index 2fb791f6a8..c6d07d1fab 100644 --- a/src/org/labkey/test/tests/LinkedSchemaTest.java +++ b/src/org/labkey/test/tests/LinkedSchemaTest.java @@ -53,7 +53,6 @@ import java.io.File; import java.io.IOException; import java.util.Arrays; -import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -646,10 +645,7 @@ void setupProject() _userHelper.createUser(EXTERNAL_SCHEMA_USER); _containerHelper.createProject(getProjectName(), null); - - // Giving this user more permissions in the test project so it can access the schema browser. - apiPermissionsHelper.setUserPermissions(EXTERNAL_SCHEMA_USER, "Folder Administrator"); - + apiPermissionsHelper.setUserPermissions(EXTERNAL_SCHEMA_USER, "Reader"); _containerHelper.createSubfolder(getProjectName(), SOURCE_FOLDER); // Enable linkedschematest in source folder so the "BPeopleTemplate" is visible. _containerHelper.enableModule("linkedschematest"); @@ -939,25 +935,29 @@ public void testCoreLinkedSchemaFilters() public void testLinkedSchemaToExternalSubfolder() throws IOException, CommandException { - String externalProject = "Linked Schema Other Project"; + String externalProject = "Linked Schema Other Project" + TRICKY_CHARACTERS_FOR_PROJECT_NAMES; log(String.format("Create a separate project %s.", externalProject)); + // Delete the external project if it already exists. _containerHelper.deleteProject(externalProject, false); _containerHelper.createProject(externalProject, null); + // Add the Experiment module to test schemas linked to exp. + _containerHelper.enableModule("Experiment"); + validateExternalLinkedSampleType(externalProject); - validateExternalLinkedExperiment(externalProject); // Flow. Is this right? + validateExternalLinkedExperiment(externalProject); validateExternalLinkedDataclass(externalProject); } private void validateExternalLinkedSampleType(String externalProject) throws IOException, CommandException { - String subFolder = "Sub Folder Sample Type"; + String subFolder = "Sub Folder " + TRICKY_CHARACTERS_FOR_PROJECT_NAMES + " Sample Type"; String subFolderPath = externalProject + "/" + subFolder; - String sampleType = "Linked Schema Test"; + String sampleType = TestDataGenerator.randomDomainName("Linked Schema Test"); String strField = TestDataGenerator.randomFieldName("Str"); log(String.format("Create sub-folder %s for the sample type test.", subFolderPath)); @@ -972,25 +972,26 @@ private void validateExternalLinkedSampleType(String externalProject) throws IOE private void validateExternalLinkedExperiment(String externalProject) { - String subFolder = "Sub Folder Experiment"; + String subFolder = "Sub Folder " + TRICKY_CHARACTERS_FOR_PROJECT_NAMES + " Experiment"; String subfolderPath = externalProject + "/" + subFolder; - createExperiment(externalProject, subFolder); + String subFolderRunGroup = "SubFolder Run Group"; + createExperiment(externalProject, subFolder, subFolderRunGroup); - List expectedValues = List.of("Example 5 Run (XTandem peptide search)"); + List expectedValues = List.of(subFolderRunGroup); String linkedExpRunSchema = "External_Exp_Run_Schema"; - validateExternalLinkedSchema(linkedExpRunSchema, subfolderPath, "flow", "Runs", "Name", expectedValues); + validateExternalLinkedSchema(linkedExpRunSchema, subfolderPath, "exp", "RunGroups", "Name", expectedValues); } private void validateExternalLinkedDataclass(String externalProject) throws IOException, CommandException { - String subFolder = "DataClass Sub Folder"; + String subFolder = "Sub Folder " + TRICKY_CHARACTERS_FOR_PROJECT_NAMES + "DataClass"; String subfolderPath = externalProject + "/" + subFolder; - String dataClassName = "Export data class"; + String dataClassName = TestDataGenerator.randomDomainName("Export data class"); String strField = TestDataGenerator.randomFieldName("Str"); log(String.format("Create sub-folder %s for the data classs test.", subfolderPath)); @@ -1047,27 +1048,22 @@ private List populateDomain(String externalProject, String subFolder, bo return subFolderValues; } - private void createExperiment(String externalProject, String subFolder) + private void createExperiment(String externalProject, String subFolder, String subFolderRunGroup) { - // This is cut-and-paste code from ExpTest setup. + // Create a RunGroup in the parent folder. + goToProjectHome(externalProject); + clickTab("Experiment"); + waitAndClickAndWait(Locator.linkContainingText("Create Run Group")); + setFormElement(Locator.name("name"), "Parent Run Group"); + clickButton("Submit"); - _containerHelper.createSubfolder(externalProject, subFolder, new String[]{"Experiment", "Query"}); - - new PortalHelper(this) - .doInAdminMode(portalHelper -> { - portalHelper.addWebPart("Data Pipeline"); - portalHelper.addWebPart("Run Groups"); - }); - clickButton("Setup"); - setPipelineRoot(TestFileUtils.getSampleData("xarfiles/expVerify").getAbsolutePath()); - clickFolder(subFolder); - - clickButton("Process and Import Data"); + _containerHelper.createSubfolder(externalProject, subFolder); - _fileBrowserHelper.importFile("experiment.xar.xml", "Import Experiment"); - Date importDate = new Date(); // Import timestamp will have various formats applied to it - clickAndWait(Locator.linkWithText("Data Pipeline")); - waitForPipelineJobsToComplete(1, false); + // Create a RunGroup in the subfolder. + clickTab("Experiment"); + waitAndClickAndWait(Locator.linkContainingText("Create Run Group")); + setFormElement(Locator.name("name"), subFolderRunGroup); + clickButton("Submit"); } @@ -1098,7 +1094,8 @@ private void validateExternalLinkedData(String linkedSchemaName, String query, S // Use a FieldInfo object to deal with any tricky characters. FieldInfo fieldInfo = new FieldInfo(fieldName, FieldDefinition.ColumnType.String); - goToSchemaBrowser(); + String url = WebTestHelper.buildURL("query", getProjectName(), "begin.view"); + beginAt(url); viewQueryData(linkedSchemaName, query); DataRegionTable table = new DataRegionTable("query", getDriver()); List actualValues = table.getColumnDataAsText(fieldInfo); From 95d791963c6613fe8eb887665731dd620eb1130c Mon Sep 17 00:00:00 2001 From: labkey-danield Date: Mon, 25 Aug 2025 15:10:52 -0700 Subject: [PATCH 6/6] Added an explicit check for Issue 53784. --- src/org/labkey/test/tests/LinkedSchemaTest.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/org/labkey/test/tests/LinkedSchemaTest.java b/src/org/labkey/test/tests/LinkedSchemaTest.java index c6d07d1fab..fa7ccf7ca3 100644 --- a/src/org/labkey/test/tests/LinkedSchemaTest.java +++ b/src/org/labkey/test/tests/LinkedSchemaTest.java @@ -988,11 +988,14 @@ private void validateExternalLinkedExperiment(String externalProject) private void validateExternalLinkedDataclass(String externalProject) throws IOException, CommandException { - String subFolder = "Sub Folder " + TRICKY_CHARACTERS_FOR_PROJECT_NAMES + "DataClass"; + String subFolder = "Sub Folder " + TRICKY_CHARACTERS_FOR_PROJECT_NAMES + " DataClass"; String subfolderPath = externalProject + "/" + subFolder; String dataClassName = TestDataGenerator.randomDomainName("Export data class"); - String strField = TestDataGenerator.randomFieldName("Str"); + + // This is the last sscenario to run. Adding this check here will allow the other scenarios to run before the test fails. + //Issue 53784: Field name with a quote causes a "QueryParseException: syntax error" when creating a linked schema. + String strField = TestDataGenerator.randomFieldName("Str \" "); log(String.format("Create sub-folder %s for the data classs test.", subfolderPath)); _containerHelper.createSubfolder(externalProject, subFolder);