Skip to content

Commit 362a2ea

Browse files
committed
Merge remote-tracking branch 'origin/develop' into fb_sharedHeaderPages
2 parents bd5b9a2 + cb2d092 commit 362a2ea

14 files changed

Lines changed: 170 additions & 93 deletions

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,4 @@ seleniumVersion=4.45.0
1414

1515
mockserverNettyVersion=5.15.0
1616

17-
labkeySchemasTestVersion=26.3-SNAPSHOT
17+
labkeySchemasTestVersion=26.7-SNAPSHOT

modules/dumbster/src/org/labkey/dumbster/DumbsterModule.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@
2020
import org.labkey.api.module.CodeOnlyModule;
2121
import org.labkey.api.module.ModuleContext;
2222
import org.labkey.api.settings.AppProps;
23-
import org.labkey.api.util.MailHelper;
24-
import org.labkey.api.util.SmtpTransportProvider;
2523
import org.labkey.api.view.BaseWebPartFactory;
2624
import org.labkey.api.view.Portal;
2725
import org.labkey.api.view.ViewContext;
@@ -67,7 +65,9 @@ public WebPartView<?> getWebPartView(@NotNull ViewContext portalCtx, @NotNull Po
6765
@Override
6866
public void doStartup(ModuleContext moduleContext)
6967
{
70-
if (MailHelper.getActiveProvider() instanceof SmtpTransportProvider && AppProps.getInstance().isMailRecorderEnabled())
68+
// The recorder installs its own SMTP provider to capture mail, so it can run regardless of how the server's
69+
// real email transport is configured.
70+
if (AppProps.getInstance().isMailRecorderEnabled())
7171
DumbsterManager.get().start();
7272
}
7373
}

modules/dumbster/src/org/labkey/dumbster/model/DumbsterManager.java

Lines changed: 32 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.apache.logging.log4j.LogManager;
2222
import org.labkey.api.settings.AppProps;
2323
import org.labkey.api.util.ContextListener;
24+
import org.labkey.api.util.EmailTransportProvider;
2425
import org.labkey.api.util.MailHelper;
2526
import org.labkey.api.util.ShutdownListener;
2627
import org.labkey.api.util.SmtpTransportProvider;
@@ -59,14 +60,15 @@ public static void setInstance(DumbsterManager instance)
5960

6061
SimpleSmtpServer _server;
6162

63+
// The transport provider that was active before the recorder installed its own; restored on stop().
64+
private EmailTransportProvider _previousProvider;
65+
66+
// True while the recorder's capture provider is installed as the active provider. Guards against overwriting
67+
// _previousProvider if start() runs again after the capture server stopped on its own (without a stop() call).
68+
private boolean _recording;
69+
6270
public boolean start()
6371
{
64-
if (!(MailHelper.getActiveProvider() instanceof SmtpTransportProvider))
65-
{
66-
_log.error("Mail recorder cannot be started: active mail provider is not SmtpTransportProvider");
67-
return false;
68-
}
69-
7072
if (_server != null && !_server.isStopped())
7173
{
7274
// We're already running, no need to spin up another, but reset the list of messages
@@ -75,56 +77,58 @@ public boolean start()
7577
}
7678

7779
int port;
78-
ServerSocket socket = null;
79-
try
80+
try (ServerSocket socket = new ServerSocket(0))
8081
{
81-
socket = new ServerSocket(0);
8282
port = socket.getLocalPort();
8383
}
8484
catch (IOException e)
8585
{
8686
_log.error("Failed to open a server socket", e);
8787
return false;
8888
}
89-
finally
89+
90+
_log.info("Connecting mail recorder to port {}", port);
91+
_server = SimpleSmtpServer.start(port);
92+
if (_server.isStopped())
9093
{
91-
try
92-
{
93-
if (socket != null)
94-
socket.close();
95-
}
96-
catch (IOException ignored) {}
94+
_log.error("Failed to connect mail recorder. Port {} may be in use.", port);
95+
_server = null;
96+
return false;
9797
}
9898

99+
// Dumbster uses its own SMTP configuration and sets the MailHelper active provider to capture all outgoing
100+
// email. Previous provider (official SMTP or Microsoft Graph configuration) is stashed and restored on stop().
99101
Properties props = new Properties();
100102
props.setProperty("mail.smtp.host", "localhost");
101103
props.setProperty("mail.smtp.user", "Anonymous");
102104
props.setProperty("mail.smtp.port", Integer.toString(port));
103-
Session session = Session.getInstance(props);
104105

105-
_log.info("Switching MailHelper to use port {}", port);
106-
MailHelper.setSmtpSession(session);
106+
SmtpTransportProvider recorderProvider = new SmtpTransportProvider();
107+
recorderProvider.configure(props);
107108

108-
_log.info("Connecting mail recorder to port {}", port);
109-
_server = SimpleSmtpServer.start(port);
110-
if (_server.isStopped())
109+
_log.info("Switching MailHelper to the mail recorder on port {}", port);
110+
if (!_recording)
111111
{
112-
_log.error("Failed to connect mail recorder. Port {} may be in use.", port);
113-
_server = null;
114-
return false;
112+
// Capture the real provider only when first entering the recording state; a restart after the capture
113+
// server stopped on its own must not overwrite it with a previously installed recorder provider.
114+
_previousProvider = MailHelper.getActiveProvider();
115+
_recording = true;
115116
}
117+
MailHelper.setActiveProvider(recorderProvider);
118+
116119
ContextListener.addShutdownListener(this);
117120
return true;
118121
}
119122

120123
public void stop()
121124
{
122-
// Stop the server, if there is one, but leave it around for
123-
// viewing until the next call to start() overwrites.
125+
// Stop the server, if there is one, but leave it around for viewing until the next call to start() overwrites.
124126
if (_server != null)
125127
{
126128
_log.info("Reverting MailHelper to {} configuration", AppProps.getInstance().getWebappConfigurationFilename());
127-
MailHelper.setSmtpSession(null);
129+
MailHelper.setActiveProvider(_previousProvider);
130+
_previousProvider = null;
131+
_recording = false;
128132

129133
_server.stop();
130134
ContextListener.removeShutdownListener(this);

src/org/labkey/test/params/assay/AssayDesign.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,15 @@
3232

3333
public abstract class AssayDesign<T extends AssayDesign<T>>
3434
{
35+
final String _name;
3536
final String _providerName;
3637
final List<Consumer<Protocol>> _transformers = new ArrayList<>();
3738

3839
protected AssayDesign(String providerName, String name)
3940
{
4041
_providerName = providerName;
4142
_transformers.add(p -> p.setName(name));
43+
_name = name;
4244
}
4345

4446
public static AssayDesign<?> of(String providerName, String name)
@@ -76,15 +78,15 @@ public T setFields(String domainName, List<PropertyDescriptor> fields, boolean k
7678

7779
public Protocol createAssay(String containerPath, Connection connection) throws IOException, CommandException
7880
{
79-
TestLogger.info(String.format("Creating %s assay in '%s'", _providerName, containerPath));
81+
TestLogger.info("Creating %s assay '%s' in '%s'".formatted(_providerName, _name, containerPath));
8082

8183
GetProtocolCommand getProtocolCommand = new GetProtocolCommand(_providerName);
8284
ProtocolResponse getProtocolResponse = getProtocolCommand.execute(connection, containerPath);
8385

8486
Protocol protocol = updateProtocol(containerPath, connection, getProtocolResponse.getProtocol());
8587

86-
TestLogger.info(String.format("Successfully created %s assay '%s' in '%s':\n%s", _providerName,
87-
protocol.getName(), containerPath, protocol.toJSONObject().toString(2)));
88+
TestLogger.log().debug(() -> String.format("Successfully created %s assay '%s':\n%s", _providerName,
89+
protocol.getName(), protocol.toJSONObject().toString(2)));
8890

8991
return protocol;
9092
}

src/org/labkey/test/tests/DataClassFolderExportImportTest.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ public void testExportImportSimpleDataClass() throws Exception
134134
ph.addWebPart("Experiment Runs");
135135
});
136136

137-
DataRegionTable sourceRunsTable = DataRegionTable.DataRegion(getDriver()).withName("Runs").waitFor();
137+
DataRegionTable sourceRunsTable = DataRegionTable.DataRegion(getDriver()).inWebPart("Experiment Runs").waitFor();
138138
List<String> runNames = sourceRunsTable.getColumnDataAsText("Name");
139139

140140
clickAndWait(Locator.linkWithText(testDataClass));
@@ -158,7 +158,7 @@ public void testExportImportSimpleDataClass() throws Exception
158158
importFolderFromZip(exportedFolderFile, false, 1);
159159
goToProjectFolder(IMPORT_PROJECT_NAME, importFolder);
160160

161-
List<String> importedRunNames = DataRegionTable.DataRegion(getDriver()).withName("Runs").waitFor()
161+
List<String> importedRunNames = DataRegionTable.DataRegion(getDriver()).inWebPart("Experiment Runs").waitFor()
162162
.getColumnDataAsText("Name");
163163
for (String sourceRun : runNames)
164164
{
@@ -172,7 +172,7 @@ public void testExportImportSimpleDataClass() throws Exception
172172
List<Map<String, String>> destRowData = destTable.getTableData();
173173

174174
// now ensure expected data in the sampleType made it to the destination folder
175-
for (Map exportedRow : sourceRowData)
175+
for (Map<String, String> exportedRow : sourceRowData)
176176
{
177177
// find the map from the exported project with the same name
178178
Map<String, String> matchingMap = destRowData.stream().filter(a-> a.get("Name").equals(exportedRow.get("Name")))
@@ -289,7 +289,7 @@ public void testExportImportMissingValueDataClass() throws Exception
289289
List<Map<String, String>> destRowData = importTable.getTableData();
290290

291291
// now ensure expected data in the sampleType made it to the destination folder
292-
for (Map exportedRow : sourceRowData)
292+
for (Map<String, String> exportedRow : sourceRowData)
293293
{
294294
// find the map from the exported project with the same name
295295
Map<String, String> matchingMap = destRowData.stream().filter(a-> a.get("Name").equals(exportedRow.get("Name")))

src/org/labkey/test/tests/SampleTypeFolderExportImportTest.java

Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717

1818
import org.apache.commons.io.FileUtils;
1919
import org.junit.Assert;
20-
import org.junit.BeforeClass;
2120
import org.junit.Test;
2221
import org.junit.experimental.categories.Category;
2322
import org.labkey.remoteapi.CommandException;
@@ -60,7 +59,7 @@
6059
import java.io.IOException;
6160
import java.util.ArrayList;
6261
import java.util.Arrays;
63-
import java.util.Collections;
62+
import java.util.Comparator;
6463
import java.util.HashMap;
6564
import java.util.List;
6665
import java.util.Map;
@@ -141,17 +140,9 @@ protected boolean areDataListEqual(List<Map<String, String>> list01, List<Map<St
141140
return false;
142141

143142
// Order the two lists so compare can be done by index and not by searching the two lists.
144-
Collections.sort(list01, (Map<String, String> o1, Map<String, String> o2)->
145-
{
146-
return o1.get("Name").compareTo(o2.get("Name"));
147-
}
148-
);
143+
list01.sort(Comparator.comparing((Map<String, String> o) -> o.get("Name")));
149144

150-
Collections.sort(list02, (Map<String, String> o1, Map<String, String> o2)->
151-
{
152-
return o1.get("Name").compareTo(o2.get("Name"));
153-
}
154-
);
145+
list02.sort(Comparator.comparing((Map<String, String> o) -> o.get("Name")));
155146

156147
boolean areEqual = true;
157148

@@ -485,11 +476,12 @@ public void testExportImportDerivedSamples() throws Exception
485476
"ParentAlias", "Parent3, Parent2", "SelfParent", "Child5", "DataClassParent", "data2, data3"));
486477
testDgen.insertRows();
487478

488-
PortalHelper portalHelper = new PortalHelper(this);
489-
portalHelper.addWebPart("Sample Types");
490-
portalHelper.addWebPart("Experiment Runs");
479+
new PortalHelper(this).doInAdminMode(portalHelper -> {
480+
portalHelper.addWebPart("Sample Types");
481+
portalHelper.addWebPart("Experiment Runs");
482+
});
491483

492-
DataRegionTable sourceRunsTable = DataRegionTable.DataRegion(getDriver()).withName("Runs").waitFor();
484+
DataRegionTable sourceRunsTable = DataRegionTable.DataRegion(getDriver()).inWebPart("Experiment Runs").waitFor();
493485
List<String> runNames = sourceRunsTable.getColumnDataAsText("Name");
494486

495487
clickAndWait(Locator.linkWithText(testSamples));
@@ -510,7 +502,7 @@ public void testExportImportDerivedSamples() throws Exception
510502
importFolderFromZip(exportedFolderFile, false, 1);
511503
goToProjectFolder(IMPORT_PROJECT_NAME, importFolder);
512504

513-
List<String> importedRunNames = DataRegionTable.DataRegion(getDriver()).withName("Runs").waitFor()
505+
List<String> importedRunNames = DataRegionTable.DataRegion(getDriver()).inWebPart("Experiment Runs").waitFor()
514506
.getColumnDataAsText("Name");
515507
for (String sourceRun : runNames)
516508
{
@@ -530,7 +522,7 @@ public void testExportImportDerivedSamples() throws Exception
530522
List<Map<String, String>> destRowData = destSamplesTable.getTableData();
531523

532524
// now ensure expected data in the sampleType made it to the destination folder
533-
for (Map exportedRow : sourceRowData)
525+
for (Map<String, String> exportedRow : sourceRowData)
534526
{
535527
// find the map from the exported project with the same name
536528
Map<String, String> matchingMap = destRowData.stream().filter(a-> a.get("Name").equals(exportedRow.get("Name")))
@@ -550,11 +542,11 @@ public void testExportImportDerivedSamples() throws Exception
550542
assertThat("expect export and import values to be equivalent",
551543
exportedRow.get(decimalColFieldKey), equalTo(matchingMap.get(decimalColFieldKey)));
552544

553-
List<String> sourceParents = Arrays.asList(exportedRow.get("Inputs/Materials/parentSamples").toString()
545+
List<String> sourceParents = Arrays.asList(exportedRow.get("Inputs/Materials/parentSamples")
554546
.replace(" ", "").split(","));
555547
String[] importedParents = matchingMap.get("Inputs/Materials/parentSamples").replace(" ", "").split(",");
556548
assertThat("expect parent sampleType derivation to round trip with equivalent values", sourceParents, hasItems(importedParents));
557-
List<String> sourceDataParents = Arrays.asList(exportedRow.get("Inputs/Data/parentDataClass").toString()
549+
List<String> sourceDataParents = Arrays.asList(exportedRow.get("Inputs/Data/parentDataClass")
558550
.replace(" ", "").split(","));
559551
String[] importedDataParents = matchingMap.get("Inputs/Data/parentDataClass").replace(" ", "").split(",");
560552
assertThat("expect parent dataClass derivation to round trip with equivalent values", sourceDataParents, hasItems(importedDataParents));
@@ -761,7 +753,7 @@ public void testExportImportSampleTypesWithAssayRuns() throws Exception
761753
importData.add(importedDataTable.getRowDataAsMap(i));
762754
}
763755

764-
for (Map exportedRow : exportData)
756+
for (Map<String, String> exportedRow : exportData)
765757
{
766758
// find the map from the exported project with the same name
767759
Map<String, String> matchingMap = importData.stream().filter(a -> a.get("sampleId").equals(exportedRow.get("sampleId")))
@@ -797,14 +789,14 @@ private FieldDefinition getFieldByNamePart(List<FieldDefinition> fields, String
797789
if (field.isNamePartMatch(namePart))
798790
return field;
799791
}
800-
return null;
792+
throw new RuntimeException("Field " + namePart + " not found: " + fields.stream().map(FieldDefinition::getName).toList());
801793
}
802794

803795
private StringBuilder checkDisplayFields(String displayField, List<String> columnLabels)
804796
{
805797
StringBuilder tmpString = new StringBuilder();
806798

807-
if(!columnLabels.contains("Name"))
799+
if(!columnLabels.contains(displayField))
808800
{
809801
String errorMsg = "Did not find the 'Name' column.";
810802
log("\n*************** ERROR ***************\n" + errorMsg + "\n*************** ERROR ***************");

src/org/labkey/test/tests/SampleTypeLinkToStudyTest.java

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,13 @@
4040
import org.labkey.test.params.FieldDefinition;
4141
import org.labkey.test.params.experiment.SampleTypeDefinition;
4242
import org.labkey.test.util.DataRegionTable;
43+
import org.labkey.test.util.DomainUtils;
4344
import org.labkey.test.util.OptionalFeatureHelper;
4445
import org.labkey.test.util.PortalHelper;
4546
import org.labkey.test.util.SampleTypeHelper;
4647
import org.labkey.test.util.StudyHelper;
4748
import org.labkey.test.util.TestDataGenerator;
49+
import org.labkey.test.util.data.TestDataUtils;
4850

4951
import java.io.File;
5052
import java.io.IOException;
@@ -82,7 +84,7 @@ public static void setupProject() throws IOException
8284
init.doSetup();
8385
}
8486

85-
private void doSetup()
87+
private void doSetup() throws IOException
8688
{
8789
_containerHelper.createProject(getProjectName(), null);
8890
_containerHelper.createProject(VISIT_BASED_STUDY, "Study");
@@ -112,7 +114,7 @@ private void doSetup()
112114

113115

114116

115-
private void createSampleTypes()
117+
private void createSampleTypes() throws IOException
116118
{
117119
SampleTypeHelper sampleHelper = new SampleTypeHelper(this);
118120

@@ -130,11 +132,12 @@ private void createSampleTypes()
130132
new FieldDefinition("ParticipantId", FieldDefinition.ColumnType.Subject))), data1);
131133

132134
goToProjectHome(SAMPLE_TYPE_PROJECT);
133-
String data2 = "Name\tVisitLabel\tVisitDate\tParticipantId\n" +
134-
"First\t" + visitLabel1 + "\t" + now + "\tP1\n" +
135-
"Second\t" + visitLabel2 + "\t" + now + "\tP2\n" +
136-
"Third\t" + visitLabel1 + "\t" + now + "\tP3\n" +
137-
"Fourth\t" + visitLabel2 + "\t" + now + "\tP4\n";
135+
File data2 = TestDataUtils.writeRowsToFile("samples.tsv", List.of(
136+
List.of("Name", "VisitLabel", "VisitDate", "ParticipantId"),
137+
List.of("First", visitLabel1, now, "P1"),
138+
List.of("Second", visitLabel2, now, "P2"),
139+
List.of("Third", visitLabel1, now, "P3"),
140+
List.of("Fourth", visitLabel2, now, "P4")));
138141

139142
sampleHelper.createSampleType(new SampleTypeDefinition(SAMPLE_TYPE2)
140143
.setFields(List.of(
@@ -777,14 +780,10 @@ public void testVisitLabelAutoLinkToStudy()
777780
public void preTest() throws Exception
778781
{
779782
//deleting the datasets from study folders.
780-
if (TestDataGenerator.doesDomainExists(DATE_BASED_STUDY, "study", "Sample type 1"))
781-
TestDataGenerator.deleteDomain(DATE_BASED_STUDY, "study", "Sample type 1");
782-
if (TestDataGenerator.doesDomainExists(DATE_BASED_STUDY, "study", "Sample type 2"))
783-
TestDataGenerator.deleteDomain(DATE_BASED_STUDY, "study", "Sample type 2");
784-
if (TestDataGenerator.doesDomainExists(VISIT_BASED_STUDY, "study", "Sample type 1"))
785-
TestDataGenerator.deleteDomain(VISIT_BASED_STUDY, "study", "Sample type 1");
786-
if (TestDataGenerator.doesDomainExists(VISIT_BASED_STUDY, "study", "Sample type 2"))
787-
TestDataGenerator.deleteDomain(VISIT_BASED_STUDY, "study", "Sample type 2");
783+
DomainUtils.ensureDeleted(DATE_BASED_STUDY, "study", "Sample type 1");
784+
DomainUtils.ensureDeleted(DATE_BASED_STUDY, "study", "Sample type 2");
785+
DomainUtils.ensureDeleted(VISIT_BASED_STUDY, "study", "Sample type 1");
786+
DomainUtils.ensureDeleted(VISIT_BASED_STUDY, "study", "Sample type 2");
788787
}
789788

790789
private void createNewVisits(String label, String startRange, String endRange)

0 commit comments

Comments
 (0)